accesskit/
lib.rs

1// Copyright 2021 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file) or the MIT license (found in
4// the LICENSE-MIT file), at your option.
5
6// Derived from Chromium's accessibility abstraction.
7// Copyright 2018 The Chromium Authors. All rights reserved.
8// Use of this source code is governed by a BSD-style license that can be
9// found in the LICENSE.chromium file.
10
11#![cfg_attr(not(any(feature = "pyo3", feature = "schemars")), no_std)]
12
13extern crate alloc;
14
15use alloc::{boxed::Box, string::String, vec::Vec};
16use core::fmt;
17#[cfg(feature = "pyo3")]
18use pyo3::pyclass;
19#[cfg(feature = "schemars")]
20use schemars::{
21    gen::SchemaGenerator,
22    schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
23    JsonSchema, Map as SchemaMap,
24};
25#[cfg(feature = "serde")]
26use serde::{
27    de::{Deserializer, IgnoredAny, MapAccess, Visitor},
28    ser::{SerializeMap, Serializer},
29    Deserialize, Serialize,
30};
31
32mod geometry;
33pub use geometry::{Affine, Point, Rect, Size, Vec2};
34
35/// The type of an accessibility node.
36///
37/// The majority of these roles come from the ARIA specification. Reference
38/// the latest draft for proper usage.
39///
40/// Like the AccessKit schema as a whole, this list is largely taken
41/// from Chromium. However, unlike Chromium's alphabetized list, this list
42/// is ordered roughly by expected usage frequency (with the notable exception
43/// of [`Role::Unknown`]). This is more efficient in serialization formats
44/// where integers use a variable-length encoding.
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
46#[cfg_attr(feature = "enumn", derive(enumn::N))]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48#[cfg_attr(feature = "schemars", derive(JsonSchema))]
49#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
50#[cfg_attr(
51    feature = "pyo3",
52    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
53)]
54#[repr(u8)]
55pub enum Role {
56    #[default]
57    Unknown,
58    TextRun,
59    Cell,
60    Label,
61    Image,
62    Link,
63    Row,
64    ListItem,
65
66    /// Contains the bullet, number, or other marker for a list item.
67    ListMarker,
68
69    TreeItem,
70    ListBoxOption,
71    MenuItem,
72    MenuListOption,
73    Paragraph,
74
75    /// A generic container that should be ignored by assistive technologies
76    /// and filtered out of platform accessibility trees. Equivalent to the ARIA
77    /// `none` or `presentation` role, or to an HTML `div` with no role.
78    GenericContainer,
79
80    CheckBox,
81    RadioButton,
82    TextInput,
83    Button,
84    DefaultButton,
85    Pane,
86    RowHeader,
87    ColumnHeader,
88    RowGroup,
89    List,
90    Table,
91    LayoutTableCell,
92    LayoutTableRow,
93    LayoutTable,
94    Switch,
95    Menu,
96
97    MultilineTextInput,
98    SearchInput,
99    DateInput,
100    DateTimeInput,
101    WeekInput,
102    MonthInput,
103    TimeInput,
104    EmailInput,
105    NumberInput,
106    PasswordInput,
107    PhoneNumberInput,
108    UrlInput,
109
110    Abbr,
111    Alert,
112    AlertDialog,
113    Application,
114    Article,
115    Audio,
116    Banner,
117    Blockquote,
118    Canvas,
119    Caption,
120    Caret,
121    Code,
122    ColorWell,
123    ComboBox,
124    EditableComboBox,
125    Complementary,
126    Comment,
127    ContentDeletion,
128    ContentInsertion,
129    ContentInfo,
130    Definition,
131    DescriptionList,
132    DescriptionListDetail,
133    DescriptionListTerm,
134    Details,
135    Dialog,
136    Directory,
137    DisclosureTriangle,
138    Document,
139    EmbeddedObject,
140    Emphasis,
141    Feed,
142    FigureCaption,
143    Figure,
144    Footer,
145    FooterAsNonLandmark,
146    Form,
147    Grid,
148    Group,
149    Header,
150    HeaderAsNonLandmark,
151    Heading,
152    Iframe,
153    IframePresentational,
154    ImeCandidate,
155    Keyboard,
156    Legend,
157    LineBreak,
158    ListBox,
159    Log,
160    Main,
161    Mark,
162    Marquee,
163    Math,
164    MenuBar,
165    MenuItemCheckBox,
166    MenuItemRadio,
167    MenuListPopup,
168    Meter,
169    Navigation,
170    Note,
171    PluginObject,
172    Portal,
173    Pre,
174    ProgressIndicator,
175    RadioGroup,
176    Region,
177    RootWebArea,
178    Ruby,
179    RubyAnnotation,
180    ScrollBar,
181    ScrollView,
182    Search,
183    Section,
184    Slider,
185    SpinButton,
186    Splitter,
187    Status,
188    Strong,
189    Suggestion,
190    SvgRoot,
191    Tab,
192    TabList,
193    TabPanel,
194    Term,
195    Time,
196    Timer,
197    TitleBar,
198    Toolbar,
199    Tooltip,
200    Tree,
201    TreeGrid,
202    Video,
203    WebView,
204    Window,
205
206    PdfActionableHighlight,
207    PdfRoot,
208
209    // ARIA Graphics module roles:
210    // https://rawgit.com/w3c/graphics-aam/master/#mapping_role_table
211    GraphicsDocument,
212    GraphicsObject,
213    GraphicsSymbol,
214
215    // DPub Roles:
216    // https://www.w3.org/TR/dpub-aam-1.0/#mapping_role_table
217    DocAbstract,
218    DocAcknowledgements,
219    DocAfterword,
220    DocAppendix,
221    DocBackLink,
222    DocBiblioEntry,
223    DocBibliography,
224    DocBiblioRef,
225    DocChapter,
226    DocColophon,
227    DocConclusion,
228    DocCover,
229    DocCredit,
230    DocCredits,
231    DocDedication,
232    DocEndnote,
233    DocEndnotes,
234    DocEpigraph,
235    DocEpilogue,
236    DocErrata,
237    DocExample,
238    DocFootnote,
239    DocForeword,
240    DocGlossary,
241    DocGlossRef,
242    DocIndex,
243    DocIntroduction,
244    DocNoteRef,
245    DocNotice,
246    DocPageBreak,
247    DocPageFooter,
248    DocPageHeader,
249    DocPageList,
250    DocPart,
251    DocPreface,
252    DocPrologue,
253    DocPullquote,
254    DocQna,
255    DocSubtitle,
256    DocTip,
257    DocToc,
258
259    /// Behaves similar to an ARIA grid but is primarily used by Chromium's
260    /// `TableView` and its subclasses, so they can be exposed correctly
261    /// on certain platforms.
262    ListGrid,
263
264    /// This is just like a multi-line document, but signals that assistive
265    /// technologies should implement behavior specific to a VT-100-style
266    /// terminal.
267    Terminal,
268}
269
270/// An action to be taken on an accessibility node.
271#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
272#[cfg_attr(feature = "enumn", derive(enumn::N))]
273#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
274#[cfg_attr(feature = "schemars", derive(JsonSchema))]
275#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
276#[cfg_attr(
277    feature = "pyo3",
278    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
279)]
280#[repr(u8)]
281pub enum Action {
282    /// Do the equivalent of a single click or tap.
283    Click,
284
285    Focus,
286    Blur,
287
288    Collapse,
289    Expand,
290
291    /// Requires [`ActionRequest::data`] to be set to [`ActionData::CustomAction`].
292    CustomAction,
293
294    /// Decrement a numeric value by one step.
295    Decrement,
296    /// Increment a numeric value by one step.
297    Increment,
298
299    HideTooltip,
300    ShowTooltip,
301
302    /// Delete any selected text in the control's text value and
303    /// insert the specified value in its place, like when typing or pasting.
304    /// Requires [`ActionRequest::data`] to be set to [`ActionData::Value`].
305    ReplaceSelectedText,
306
307    // Scrolls by approximately one screen in a specific direction.
308    // TBD: Do we need a doc comment on each of the values below?
309    // Or does this awkwardness suggest a refactor?
310    ScrollBackward,
311    ScrollDown,
312    ScrollForward,
313    ScrollLeft,
314    ScrollRight,
315    ScrollUp,
316
317    /// Scroll any scrollable containers to make the target object visible
318    /// on the screen.  Optionally set [`ActionRequest::data`] to
319    /// [`ActionData::ScrollTargetRect`].
320    ScrollIntoView,
321
322    /// Scroll the given object to a specified point in the tree's container
323    /// (e.g. window). Requires [`ActionRequest::data`] to be set to
324    /// [`ActionData::ScrollToPoint`].
325    ScrollToPoint,
326
327    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetScrollOffset`].
328    SetScrollOffset,
329
330    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetTextSelection`].
331    SetTextSelection,
332
333    /// Don't focus this node, but set it as the sequential focus navigation
334    /// starting point, so that pressing Tab moves to the next element
335    /// following this one, for example.
336    SetSequentialFocusNavigationStartingPoint,
337
338    /// Replace the value of the control with the specified value and
339    /// reset the selection, if applicable. Requires [`ActionRequest::data`]
340    /// to be set to [`ActionData::Value`] or [`ActionData::NumericValue`].
341    SetValue,
342
343    ShowContextMenu,
344}
345
346impl Action {
347    fn mask(self) -> u32 {
348        1 << (self as u8)
349    }
350
351    #[cfg(not(feature = "enumn"))]
352    fn n(value: u8) -> Option<Self> {
353        // Manually implement something similar to the enumn crate. We don't
354        // want to bring this crate by default though and we can't use a
355        // macro as it would break C bindings header file generation.
356        match value {
357            0 => Some(Action::Click),
358            1 => Some(Action::Focus),
359            2 => Some(Action::Blur),
360            3 => Some(Action::Collapse),
361            4 => Some(Action::Expand),
362            5 => Some(Action::CustomAction),
363            6 => Some(Action::Decrement),
364            7 => Some(Action::Increment),
365            8 => Some(Action::HideTooltip),
366            9 => Some(Action::ShowTooltip),
367            10 => Some(Action::ReplaceSelectedText),
368            11 => Some(Action::ScrollBackward),
369            12 => Some(Action::ScrollDown),
370            13 => Some(Action::ScrollForward),
371            14 => Some(Action::ScrollLeft),
372            15 => Some(Action::ScrollRight),
373            16 => Some(Action::ScrollUp),
374            17 => Some(Action::ScrollIntoView),
375            18 => Some(Action::ScrollToPoint),
376            19 => Some(Action::SetScrollOffset),
377            20 => Some(Action::SetTextSelection),
378            21 => Some(Action::SetSequentialFocusNavigationStartingPoint),
379            22 => Some(Action::SetValue),
380            23 => Some(Action::ShowContextMenu),
381            _ => None,
382        }
383    }
384}
385
386fn action_mask_to_action_vec(mask: u32) -> Vec<Action> {
387    let mut actions = Vec::new();
388    let mut i = 0;
389    while let Some(variant) = Action::n(i) {
390        if mask & variant.mask() != 0 {
391            actions.push(variant);
392        }
393        i += 1;
394    }
395    actions
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
399#[cfg_attr(feature = "enumn", derive(enumn::N))]
400#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
401#[cfg_attr(feature = "schemars", derive(JsonSchema))]
402#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
403#[cfg_attr(
404    feature = "pyo3",
405    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
406)]
407#[repr(u8)]
408pub enum Orientation {
409    /// E.g. most toolbars and separators.
410    Horizontal,
411    /// E.g. menu or combo box.
412    Vertical,
413}
414
415#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
416#[cfg_attr(feature = "enumn", derive(enumn::N))]
417#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
418#[cfg_attr(feature = "schemars", derive(JsonSchema))]
419#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
420#[cfg_attr(
421    feature = "pyo3",
422    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
423)]
424#[repr(u8)]
425pub enum TextDirection {
426    LeftToRight,
427    RightToLeft,
428    TopToBottom,
429    BottomToTop,
430}
431
432/// Indicates if a form control has invalid input or if a web DOM element has an
433/// [`aria-invalid`] attribute.
434///
435/// [`aria-invalid`]: https://www.w3.org/TR/wai-aria-1.1/#aria-invalid
436#[derive(Clone, Copy, Debug, PartialEq, Eq)]
437#[cfg_attr(feature = "enumn", derive(enumn::N))]
438#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
439#[cfg_attr(feature = "schemars", derive(JsonSchema))]
440#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
441#[cfg_attr(
442    feature = "pyo3",
443    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
444)]
445#[repr(u8)]
446pub enum Invalid {
447    True,
448    Grammar,
449    Spelling,
450}
451
452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
453#[cfg_attr(feature = "enumn", derive(enumn::N))]
454#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
455#[cfg_attr(feature = "schemars", derive(JsonSchema))]
456#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
457#[cfg_attr(
458    feature = "pyo3",
459    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
460)]
461#[repr(u8)]
462pub enum Toggled {
463    False,
464    True,
465    Mixed,
466}
467
468#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
469#[cfg_attr(feature = "enumn", derive(enumn::N))]
470#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
471#[cfg_attr(feature = "schemars", derive(JsonSchema))]
472#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
473#[cfg_attr(
474    feature = "pyo3",
475    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
476)]
477#[repr(u8)]
478pub enum SortDirection {
479    Ascending,
480    Descending,
481    Other,
482}
483
484#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
485#[cfg_attr(feature = "enumn", derive(enumn::N))]
486#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
487#[cfg_attr(feature = "schemars", derive(JsonSchema))]
488#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
489#[cfg_attr(
490    feature = "pyo3",
491    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
492)]
493#[repr(u8)]
494pub enum AriaCurrent {
495    False,
496    True,
497    Page,
498    Step,
499    Location,
500    Date,
501    Time,
502}
503
504#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
505#[cfg_attr(feature = "enumn", derive(enumn::N))]
506#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
507#[cfg_attr(feature = "schemars", derive(JsonSchema))]
508#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
509#[cfg_attr(
510    feature = "pyo3",
511    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
512)]
513#[repr(u8)]
514pub enum AutoComplete {
515    Inline,
516    List,
517    Both,
518}
519
520#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
521#[cfg_attr(feature = "enumn", derive(enumn::N))]
522#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
523#[cfg_attr(feature = "schemars", derive(JsonSchema))]
524#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
525#[cfg_attr(
526    feature = "pyo3",
527    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
528)]
529#[repr(u8)]
530pub enum Live {
531    Off,
532    Polite,
533    Assertive,
534}
535
536#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
537#[cfg_attr(feature = "enumn", derive(enumn::N))]
538#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
539#[cfg_attr(feature = "schemars", derive(JsonSchema))]
540#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
541#[cfg_attr(
542    feature = "pyo3",
543    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
544)]
545#[repr(u8)]
546pub enum HasPopup {
547    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", eq)
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", eq)
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", eq)
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", eq)
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 From<&FrozenNode> for Node {
1386    fn from(node: &FrozenNode) -> Self {
1387        Self {
1388            role: node.role,
1389            actions: node.actions,
1390            flags: node.flags,
1391            properties: Properties {
1392                indices: node.properties.indices,
1393                values: node.properties.values.to_vec(),
1394            },
1395        }
1396    }
1397}
1398
1399impl FrozenNode {
1400    #[inline]
1401    pub fn role(&self) -> Role {
1402        self.role
1403    }
1404}
1405
1406impl Node {
1407    #[inline]
1408    pub fn role(&self) -> Role {
1409        self.role
1410    }
1411    #[inline]
1412    pub fn set_role(&mut self, value: Role) {
1413        self.role = value;
1414    }
1415}
1416
1417impl FrozenNode {
1418    #[inline]
1419    pub fn supports_action(&self, action: Action) -> bool {
1420        (self.actions & action.mask()) != 0
1421    }
1422}
1423
1424impl Node {
1425    #[inline]
1426    pub fn supports_action(&self, action: Action) -> bool {
1427        (self.actions & action.mask()) != 0
1428    }
1429    #[inline]
1430    pub fn add_action(&mut self, action: Action) {
1431        self.actions |= action.mask();
1432    }
1433    #[inline]
1434    pub fn remove_action(&mut self, action: Action) {
1435        self.actions &= !(action.mask());
1436    }
1437    #[inline]
1438    pub fn clear_actions(&mut self) {
1439        self.actions = 0;
1440    }
1441}
1442
1443flag_methods! {
1444    /// Exclude this node and its descendants from the tree presented to
1445    /// assistive technologies, and from hit testing.
1446    (Hidden, is_hidden, set_hidden, clear_hidden),
1447    (Linked, is_linked, set_linked, clear_linked),
1448    (Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
1449    (Required, is_required, set_required, clear_required),
1450    (Visited, is_visited, set_visited, clear_visited),
1451    (Busy, is_busy, set_busy, clear_busy),
1452    (LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
1453    /// If a dialog box is marked as explicitly modal.
1454    (Modal, is_modal, set_modal, clear_modal),
1455    /// This element allows touches to be passed through when a screen reader
1456    /// is in touch exploration mode, e.g. a virtual keyboard normally
1457    /// behaves this way.
1458    (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1459    /// Use for a text widget that allows focus/selection but not input.
1460    (ReadOnly, is_read_only, set_read_only, clear_read_only),
1461    /// Use for a control or group of controls that disallows input.
1462    (Disabled, is_disabled, set_disabled, clear_disabled),
1463    (Bold, is_bold, set_bold, clear_bold),
1464    (Italic, is_italic, set_italic, clear_italic),
1465    /// Indicates that this node clips its children, i.e. may have
1466    /// `overflow: hidden` or clip children by default.
1467    (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1468    /// Indicates whether this node causes a hard line-break
1469    /// (e.g. block level elements, or `<br>`).
1470    (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1471    /// Indicates whether this node causes a page break.
1472    (IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
1473    (IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
1474    (IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
1475    (IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
1476    (IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
1477}
1478
1479option_ref_type_getters! {
1480    (get_affine_property, Affine, Affine),
1481    (get_string_property, str, String),
1482    (get_coord_slice_property, [f32], CoordSlice),
1483    (get_text_selection_property, TextSelection, TextSelection)
1484}
1485
1486slice_type_getters! {
1487    (get_length_slice_property, u8, LengthSlice)
1488}
1489
1490copy_type_getters! {
1491    (get_rect_property, Rect, Rect),
1492    (get_node_id_property, NodeId, NodeId),
1493    (get_f64_property, f64, F64),
1494    (get_usize_property, usize, Usize),
1495    (get_color_property, u32, Color),
1496    (get_text_decoration_property, TextDecoration, TextDecoration),
1497    (get_bool_property, bool, Bool)
1498}
1499
1500box_type_setters! {
1501    (set_affine_property, Affine, Affine),
1502    (set_string_property, str, String),
1503    (set_length_slice_property, [u8], LengthSlice),
1504    (set_coord_slice_property, [f32], CoordSlice),
1505    (set_text_selection_property, TextSelection, TextSelection)
1506}
1507
1508copy_type_setters! {
1509    (set_rect_property, Rect, Rect),
1510    (set_node_id_property, NodeId, NodeId),
1511    (set_f64_property, f64, F64),
1512    (set_usize_property, usize, Usize),
1513    (set_color_property, u32, Color),
1514    (set_text_decoration_property, TextDecoration, TextDecoration),
1515    (set_bool_property, bool, Bool)
1516}
1517
1518vec_type_methods! {
1519    (NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
1520    (CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
1521}
1522
1523node_id_vec_property_methods! {
1524    (Children, children, set_children, push_child, clear_children),
1525    (Controls, controls, set_controls, push_controlled, clear_controls),
1526    (Details, details, set_details, push_detail, clear_details),
1527    (DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
1528    (FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
1529    (LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
1530    /// As with the `aria-owns` property in ARIA, this property should be set
1531    /// only if the nodes referenced in the property are not descendants
1532    /// of the owning node in the AccessKit tree. In the common case, where the
1533    /// owned nodes are direct children or indirect descendants, this property
1534    /// is unnecessary.
1535    (Owns, owns, set_owns, push_owned, clear_owns),
1536    /// On radio buttons this should be set to a list of all of the buttons
1537    /// in the same group as this one, including this radio button itself.
1538    (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
1539}
1540
1541node_id_property_methods! {
1542    (ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
1543    (ErrorMessage, error_message, set_error_message, clear_error_message),
1544    (InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
1545    (MemberOf, member_of, set_member_of, clear_member_of),
1546    (NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
1547    (PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
1548    (PopupFor, popup_for, set_popup_for, clear_popup_for)
1549}
1550
1551string_property_methods! {
1552    /// The label of a control that can have a label. If the label is specified
1553    /// via the [`Node::labelled_by`] relation, this doesn't need to be set.
1554    /// Note that the text content of a node with the [`Role::Label`] role
1555    /// should be provided via [`Node::value`], not this property.
1556    (Label, label, set_label, clear_label),
1557    (Description, description, set_description, clear_description),
1558    (Value, value, set_value, clear_value),
1559    /// A single character, usually part of this node's name, that can be pressed,
1560    /// possibly along with a platform-specific modifier, to perform
1561    /// this node's default action. For menu items, the access key is only active
1562    /// while the menu is active, in contrast with [`keyboard_shortcut`];
1563    /// a single menu item may in fact have both properties.
1564    ///
1565    /// [`keyboard_shortcut`]: Node::keyboard_shortcut
1566    (AccessKey, access_key, set_access_key, clear_access_key),
1567    /// A way for application authors to identify this node for automated
1568    /// testing purpose. The value must be unique among this node's siblings.
1569    (AuthorId, author_id, set_author_id, clear_author_id),
1570    (ClassName, class_name, set_class_name, clear_class_name),
1571    /// Only present when different from parent.
1572    (FontFamily, font_family, set_font_family, clear_font_family),
1573    (HtmlTag, html_tag, set_html_tag, clear_html_tag),
1574    /// Inner HTML of an element. Only used for a top-level math element,
1575    /// to support third-party math accessibility products that parse MathML.
1576    (InnerHtml, inner_html, set_inner_html, clear_inner_html),
1577    /// A keystroke or sequence of keystrokes, complete with any required
1578    /// modifiers(s), that will perform this node's default action.
1579    /// The value of this property should be in a human-friendly format.
1580    (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
1581    /// Only present when different from parent.
1582    (Language, language, set_language, clear_language),
1583    /// If a text input has placeholder text, it should be exposed
1584    /// through this property rather than [`label`].
1585    ///
1586    /// [`label`]: Node::label
1587    (Placeholder, placeholder, set_placeholder, clear_placeholder),
1588    /// An optional string that may override an assistive technology's
1589    /// description of the node's role. Only provide this for custom control types.
1590    /// The value of this property should be in a human-friendly, localized format.
1591    (RoleDescription, role_description, set_role_description, clear_role_description),
1592    /// An optional string that may override an assistive technology's
1593    /// description of the node's state, replacing default strings such as
1594    /// "checked" or "selected". Note that most platform accessibility APIs
1595    /// and assistive technologies do not support this feature.
1596    (StateDescription, state_description, set_state_description, clear_state_description),
1597    /// If a node's only accessible name comes from a tooltip, it should be
1598    /// exposed through this property rather than [`label`].
1599    ///
1600    /// [`label`]: Node::label
1601    (Tooltip, tooltip, set_tooltip, clear_tooltip),
1602    (Url, url, set_url, clear_url),
1603    (RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
1604    (ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text)
1605}
1606
1607f64_property_methods! {
1608    (ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
1609    (ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
1610    (ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
1611    (ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
1612    (ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
1613    (ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
1614    (NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
1615    (MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
1616    (MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
1617    (NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
1618    (NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump),
1619    /// Font size is in pixels.
1620    (FontSize, font_size, set_font_size, clear_font_size),
1621    /// Font weight can take on any arbitrary numeric value. Increments of 100 in
1622    /// range `[0, 900]` represent keywords such as light, normal, bold, etc.
1623    (FontWeight, font_weight, set_font_weight, clear_font_weight)
1624}
1625
1626usize_property_methods! {
1627    (RowCount, row_count, set_row_count, clear_row_count),
1628    (ColumnCount, column_count, set_column_count, clear_column_count),
1629    (RowIndex, row_index, set_row_index, clear_row_index),
1630    (ColumnIndex, column_index, set_column_index, clear_column_index),
1631    (RowSpan, row_span, set_row_span, clear_row_span),
1632    (ColumnSpan, column_span, set_column_span, clear_column_span),
1633    (Level, level, set_level, clear_level),
1634    /// For containers like [`Role::ListBox`], specifies the total number of items.
1635    (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
1636    /// For items like [`Role::ListBoxOption`], specifies their index in the item list.
1637    /// This may not exceed the value of [`size_of_set`] as set on the container.
1638    ///
1639    /// [`size_of_set`]: Node::size_of_set
1640    (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
1641}
1642
1643color_property_methods! {
1644    /// For [`Role::ColorWell`], specifies the selected color in RGBA.
1645    (ColorValue, color_value, set_color_value, clear_color_value),
1646    /// Background color in RGBA.
1647    (BackgroundColor, background_color, set_background_color, clear_background_color),
1648    /// Foreground color in RGBA.
1649    (ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
1650}
1651
1652text_decoration_property_methods! {
1653    (Overline, overline, set_overline, clear_overline),
1654    (Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
1655    (Underline, underline, set_underline, clear_underline)
1656}
1657
1658length_slice_property_methods! {
1659    /// For text runs, the length (non-inclusive) of each character
1660    /// in UTF-8 code units (bytes). The sum of these lengths must equal
1661    /// the length of [`value`], also in bytes.
1662    ///
1663    /// A character is defined as the smallest unit of text that
1664    /// can be selected. This isn't necessarily a single Unicode
1665    /// scalar value (code point). This is why AccessKit can't compute
1666    /// the lengths of the characters from the text itself; this information
1667    /// must be provided by the text editing implementation.
1668    ///
1669    /// If this node is the last text run in a line that ends with a hard
1670    /// line break, that line break should be included at the end of this
1671    /// node's value as either a CRLF or LF; in both cases, the line break
1672    /// should be counted as a single character for the sake of this slice.
1673    /// When the caret is at the end of such a line, the focus of the text
1674    /// selection should be on the line break, not after it.
1675    ///
1676    /// [`value`]: Node::value
1677    (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
1678
1679    /// For text runs, the length of each word in characters, as defined
1680    /// in [`character_lengths`]. The sum of these lengths must equal
1681    /// the length of [`character_lengths`].
1682    ///
1683    /// The end of each word is the beginning of the next word; there are no
1684    /// characters that are not considered part of a word. Trailing whitespace
1685    /// is typically considered part of the word that precedes it, while
1686    /// a line's leading whitespace is considered its own word. Whether
1687    /// punctuation is considered a separate word or part of the preceding
1688    /// word depends on the particular text editing implementation.
1689    /// Some editors may have their own definition of a word; for example,
1690    /// in an IDE, words may correspond to programming language tokens.
1691    ///
1692    /// Not all assistive technologies require information about word
1693    /// boundaries, and not all platform accessibility APIs even expose
1694    /// this information, but for assistive technologies that do use
1695    /// this information, users will get unpredictable results if the word
1696    /// boundaries exposed by the accessibility tree don't match
1697    /// the editor's behavior. This is why AccessKit does not determine
1698    /// word boundaries itself.
1699    ///
1700    /// [`character_lengths`]: Node::character_lengths
1701    (WordLengths, word_lengths, set_word_lengths, clear_word_lengths)
1702}
1703
1704coord_slice_property_methods! {
1705    /// For text runs, this is the position of each character within
1706    /// the node's bounding box, in the direction given by
1707    /// [`text_direction`], in the coordinate space of this node.
1708    ///
1709    /// When present, the length of this slice should be the same as the length
1710    /// of [`character_lengths`], including for lines that end
1711    /// with a hard line break. The position of such a line break should
1712    /// be the position where an end-of-paragraph marker would be rendered.
1713    ///
1714    /// This property is optional. Without it, AccessKit can't support some
1715    /// use cases, such as screen magnifiers that track the caret position
1716    /// or screen readers that display a highlight cursor. However,
1717    /// most text functionality still works without this information.
1718    ///
1719    /// [`text_direction`]: Node::text_direction
1720    /// [`character_lengths`]: Node::character_lengths
1721    (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
1722
1723    /// For text runs, this is the advance width of each character,
1724    /// in the direction given by [`text_direction`], in the coordinate
1725    /// space of this node.
1726    ///
1727    /// When present, the length of this slice should be the same as the length
1728    /// of [`character_lengths`], including for lines that end
1729    /// with a hard line break. The width of such a line break should
1730    /// be non-zero if selecting the line break by itself results in
1731    /// a visible highlight (as in Microsoft Word), or zero if not
1732    /// (as in Windows Notepad).
1733    ///
1734    /// This property is optional. Without it, AccessKit can't support some
1735    /// use cases, such as screen magnifiers that track the caret position
1736    /// or screen readers that display a highlight cursor. However,
1737    /// most text functionality still works without this information.
1738    ///
1739    /// [`text_direction`]: Node::text_direction
1740    /// [`character_lengths`]: Node::character_lengths
1741    (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
1742}
1743
1744bool_property_methods! {
1745    /// Whether this node is expanded, collapsed, or neither.
1746    ///
1747    /// Setting this to `false` means the node is collapsed; omitting it means this state
1748    /// isn't applicable.
1749    (Expanded, is_expanded, set_expanded, clear_expanded),
1750
1751    /// Indicates whether this node is selected or unselected.
1752    ///
1753    /// The absence of this flag (as opposed to a `false` setting)
1754    /// means that the concept of "selected" doesn't apply.
1755    /// When deciding whether to set the flag to false or omit it,
1756    /// consider whether it would be appropriate for a screen reader
1757    /// to announce "not selected". The ambiguity of this flag
1758    /// in platform accessibility APIs has made extraneous
1759    /// "not selected" announcements a common annoyance.
1760    (Selected, is_selected, set_selected, clear_selected)
1761}
1762
1763unique_enum_property_methods! {
1764    (Invalid, invalid, set_invalid, clear_invalid),
1765    (Toggled, toggled, set_toggled, clear_toggled),
1766    (Live, live, set_live, clear_live),
1767    (TextDirection, text_direction, set_text_direction, clear_text_direction),
1768    (Orientation, orientation, set_orientation, clear_orientation),
1769    (SortDirection, sort_direction, set_sort_direction, clear_sort_direction),
1770    (AriaCurrent, aria_current, set_aria_current, clear_aria_current),
1771    (AutoComplete, auto_complete, set_auto_complete, clear_auto_complete),
1772    (HasPopup, has_popup, set_has_popup, clear_has_popup),
1773    /// The list style type. Only available on list items.
1774    (ListStyle, list_style, set_list_style, clear_list_style),
1775    (TextAlign, text_align, set_text_align, clear_text_align),
1776    (VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset)
1777}
1778
1779property_methods! {
1780    /// An affine transform to apply to any coordinates within this node
1781    /// and its descendants, including the [`bounds`] property of this node.
1782    /// The combined transforms of this node and its ancestors define
1783    /// the coordinate space of this node. /// This should be `None` if
1784    /// it would be set to the identity transform, which should be the case
1785    /// for most nodes.
1786    ///
1787    /// AccessKit expects the final transformed coordinates to be relative
1788    /// to the origin of the tree's container (e.g. window), in physical
1789    /// pixels, with the y coordinate being top-down.
1790    ///
1791    /// [`bounds`]: Node::bounds
1792    (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
1793
1794    /// The bounding box of this node, in the node's coordinate space.
1795    /// This property does not affect the coordinate space of either this node
1796    /// or its descendants; only the [`transform`] property affects that.
1797    /// This, along with the recommendation that most nodes should have
1798    /// a [`transform`] of `None`, implies that the `bounds` property
1799    /// of most nodes should be in the coordinate space of the nearest ancestor
1800    /// with a non-`None` [`transform`], or if there is no such ancestor,
1801    /// the tree's container (e.g. window).
1802    ///
1803    /// [`transform`]: Node::transform
1804    (Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
1805
1806    (TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection)
1807}
1808
1809impl FrozenNode {
1810    option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection,] }
1811}
1812
1813impl Node {
1814    option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection,] }
1815}
1816
1817vec_property_methods! {
1818    (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)
1819}
1820
1821impl fmt::Debug for FrozenNode {
1822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1823        let mut fmt = f.debug_struct("FrozenNode");
1824
1825        fmt.field("role", &self.role());
1826
1827        let supported_actions = action_mask_to_action_vec(self.actions);
1828        if !supported_actions.is_empty() {
1829            fmt.field("actions", &supported_actions);
1830        }
1831
1832        self.debug_flag_properties(&mut fmt);
1833        self.debug_node_id_vec_properties(&mut fmt);
1834        self.debug_node_id_properties(&mut fmt);
1835        self.debug_string_properties(&mut fmt);
1836        self.debug_f64_properties(&mut fmt);
1837        self.debug_usize_properties(&mut fmt);
1838        self.debug_color_properties(&mut fmt);
1839        self.debug_text_decoration_properties(&mut fmt);
1840        self.debug_length_slice_properties(&mut fmt);
1841        self.debug_coord_slice_properties(&mut fmt);
1842        self.debug_bool_properties(&mut fmt);
1843        self.debug_unique_enum_properties(&mut fmt);
1844        self.debug_option_properties(&mut fmt);
1845
1846        let custom_actions = self.custom_actions();
1847        if !custom_actions.is_empty() {
1848            fmt.field("custom_actions", &custom_actions);
1849        }
1850
1851        fmt.finish()
1852    }
1853}
1854
1855impl fmt::Debug for Node {
1856    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1857        let mut fmt = f.debug_struct("Node");
1858
1859        fmt.field("role", &self.role());
1860
1861        let supported_actions = action_mask_to_action_vec(self.actions);
1862        if !supported_actions.is_empty() {
1863            fmt.field("actions", &supported_actions);
1864        }
1865
1866        self.debug_flag_properties(&mut fmt);
1867        self.debug_node_id_vec_properties(&mut fmt);
1868        self.debug_node_id_properties(&mut fmt);
1869        self.debug_string_properties(&mut fmt);
1870        self.debug_f64_properties(&mut fmt);
1871        self.debug_usize_properties(&mut fmt);
1872        self.debug_color_properties(&mut fmt);
1873        self.debug_text_decoration_properties(&mut fmt);
1874        self.debug_length_slice_properties(&mut fmt);
1875        self.debug_coord_slice_properties(&mut fmt);
1876        self.debug_bool_properties(&mut fmt);
1877        self.debug_unique_enum_properties(&mut fmt);
1878        self.debug_option_properties(&mut fmt);
1879
1880        let custom_actions = self.custom_actions();
1881        if !custom_actions.is_empty() {
1882            fmt.field("custom_actions", &custom_actions);
1883        }
1884
1885        fmt.finish()
1886    }
1887}
1888
1889#[cfg(feature = "serde")]
1890macro_rules! serialize_property {
1891    ($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
1892        match &$self.values[$index as usize] {
1893            PropertyValue::None => (),
1894            $(PropertyValue::$variant(value) => {
1895                $map.serialize_entry(&$id, &value)?;
1896            })*
1897        }
1898    }
1899}
1900
1901#[cfg(feature = "serde")]
1902macro_rules! deserialize_property {
1903    ($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
1904        match $key {
1905            $($(PropertyId::$id => {
1906                let value = $map.next_value()?;
1907                $props.set(PropertyId::$id, PropertyValue::$type(value));
1908            })*)*
1909            PropertyId::Unset => {
1910                let _ = $map.next_value::<IgnoredAny>()?;
1911            }
1912        }
1913    }
1914}
1915
1916#[cfg(feature = "serde")]
1917impl Serialize for Properties {
1918    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1919    where
1920        S: Serializer,
1921    {
1922        let mut len = 0;
1923        for value in &*self.values {
1924            if !matches!(*value, PropertyValue::None) {
1925                len += 1;
1926            }
1927        }
1928        let mut map = serializer.serialize_map(Some(len))?;
1929        for (id, index) in self.indices.0.iter().copied().enumerate() {
1930            if index == PropertyId::Unset as u8 {
1931                continue;
1932            }
1933            let id = PropertyId::n(id as _).unwrap();
1934            serialize_property!(self, map, index, id, {
1935                NodeIdVec,
1936                NodeId,
1937                String,
1938                F64,
1939                Usize,
1940                Color,
1941                TextDecoration,
1942                LengthSlice,
1943                CoordSlice,
1944                Bool,
1945                Invalid,
1946                Toggled,
1947                Live,
1948                TextDirection,
1949                Orientation,
1950                SortDirection,
1951                AriaCurrent,
1952                AutoComplete,
1953                HasPopup,
1954                ListStyle,
1955                TextAlign,
1956                VerticalOffset,
1957                Affine,
1958                Rect,
1959                TextSelection,
1960                CustomActionVec
1961            });
1962        }
1963        map.end()
1964    }
1965}
1966
1967#[cfg(feature = "serde")]
1968struct PropertiesVisitor;
1969
1970#[cfg(feature = "serde")]
1971impl<'de> Visitor<'de> for PropertiesVisitor {
1972    type Value = Properties;
1973
1974    #[inline]
1975    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1976        formatter.write_str("property map")
1977    }
1978
1979    fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
1980    where
1981        V: MapAccess<'de>,
1982    {
1983        let mut props = Properties::default();
1984        while let Some(id) = map.next_key()? {
1985            deserialize_property!(props, map, id, {
1986                NodeIdVec {
1987                    Children,
1988                    Controls,
1989                    Details,
1990                    DescribedBy,
1991                    FlowTo,
1992                    LabelledBy,
1993                    Owns,
1994                    RadioGroup
1995                },
1996                NodeId {
1997                    ActiveDescendant,
1998                    ErrorMessage,
1999                    InPageLinkTarget,
2000                    MemberOf,
2001                    NextOnLine,
2002                    PreviousOnLine,
2003                    PopupFor
2004                },
2005                String {
2006                    Label,
2007                    Description,
2008                    Value,
2009                    AccessKey,
2010                    AuthorId,
2011                    ClassName,
2012                    FontFamily,
2013                    HtmlTag,
2014                    InnerHtml,
2015                    KeyboardShortcut,
2016                    Language,
2017                    Placeholder,
2018                    RoleDescription,
2019                    StateDescription,
2020                    Tooltip,
2021                    Url,
2022                    RowIndexText,
2023                    ColumnIndexText
2024                },
2025                F64 {
2026                    ScrollX,
2027                    ScrollXMin,
2028                    ScrollXMax,
2029                    ScrollY,
2030                    ScrollYMin,
2031                    ScrollYMax,
2032                    NumericValue,
2033                    MinNumericValue,
2034                    MaxNumericValue,
2035                    NumericValueStep,
2036                    NumericValueJump,
2037                    FontSize,
2038                    FontWeight
2039                },
2040                Usize {
2041                    RowCount,
2042                    ColumnCount,
2043                    RowIndex,
2044                    ColumnIndex,
2045                    RowSpan,
2046                    ColumnSpan,
2047                    Level,
2048                    SizeOfSet,
2049                    PositionInSet
2050                },
2051                Color {
2052                    ColorValue,
2053                    BackgroundColor,
2054                    ForegroundColor
2055                },
2056                TextDecoration {
2057                    Overline,
2058                    Strikethrough,
2059                    Underline
2060                },
2061                LengthSlice {
2062                    CharacterLengths,
2063                    WordLengths
2064                },
2065                CoordSlice {
2066                    CharacterPositions,
2067                    CharacterWidths
2068                },
2069                Bool {
2070                    Expanded,
2071                    Selected
2072                },
2073                Invalid { Invalid },
2074                Toggled { Toggled },
2075                Live { Live },
2076                TextDirection { TextDirection },
2077                Orientation { Orientation },
2078                SortDirection { SortDirection },
2079                AriaCurrent { AriaCurrent },
2080                AutoComplete { AutoComplete },
2081                HasPopup { HasPopup },
2082                ListStyle { ListStyle },
2083                TextAlign { TextAlign },
2084                VerticalOffset { VerticalOffset },
2085                Affine { Transform },
2086                Rect { Bounds },
2087                TextSelection { TextSelection },
2088                CustomActionVec { CustomActions }
2089            });
2090        }
2091
2092        Ok(props)
2093    }
2094}
2095
2096#[cfg(feature = "serde")]
2097impl<'de> Deserialize<'de> for Properties {
2098    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2099    where
2100        D: Deserializer<'de>,
2101    {
2102        deserializer.deserialize_map(PropertiesVisitor)
2103    }
2104}
2105
2106#[cfg(feature = "schemars")]
2107macro_rules! add_schema_property {
2108    ($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
2109        let name = format!("{:?}", $enum_value);
2110        let name = name[..1].to_ascii_lowercase() + &name[1..];
2111        let subschema = $gen.subschema_for::<$type>();
2112        $properties.insert(name, subschema);
2113    }};
2114}
2115
2116#[cfg(feature = "schemars")]
2117macro_rules! add_properties_to_schema {
2118    ($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
2119        $($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
2120    }
2121}
2122
2123#[cfg(feature = "schemars")]
2124impl JsonSchema for Properties {
2125    #[inline]
2126    fn schema_name() -> String {
2127        "Properties".into()
2128    }
2129
2130    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
2131        let mut properties = SchemaMap::<String, Schema>::new();
2132        add_properties_to_schema!(gen, properties, {
2133            Vec<NodeId> {
2134                Children,
2135                Controls,
2136                Details,
2137                DescribedBy,
2138                FlowTo,
2139                LabelledBy,
2140                Owns,
2141                RadioGroup
2142            },
2143            NodeId {
2144                ActiveDescendant,
2145                ErrorMessage,
2146                InPageLinkTarget,
2147                MemberOf,
2148                NextOnLine,
2149                PreviousOnLine,
2150                PopupFor
2151            },
2152            Box<str> {
2153                Label,
2154                Description,
2155                Value,
2156                AccessKey,
2157                AuthorId,
2158                ClassName,
2159                FontFamily,
2160                HtmlTag,
2161                InnerHtml,
2162                KeyboardShortcut,
2163                Language,
2164                Placeholder,
2165                RoleDescription,
2166                StateDescription,
2167                Tooltip,
2168                Url,
2169                RowIndexText,
2170                ColumnIndexText
2171            },
2172            f64 {
2173                ScrollX,
2174                ScrollXMin,
2175                ScrollXMax,
2176                ScrollY,
2177                ScrollYMin,
2178                ScrollYMax,
2179                NumericValue,
2180                MinNumericValue,
2181                MaxNumericValue,
2182                NumericValueStep,
2183                NumericValueJump,
2184                FontSize,
2185                FontWeight
2186            },
2187            usize {
2188                RowCount,
2189                ColumnCount,
2190                RowIndex,
2191                ColumnIndex,
2192                RowSpan,
2193                ColumnSpan,
2194                Level,
2195                SizeOfSet,
2196                PositionInSet
2197            },
2198            u32 {
2199                ColorValue,
2200                BackgroundColor,
2201                ForegroundColor
2202            },
2203            TextDecoration {
2204                Overline,
2205                Strikethrough,
2206                Underline
2207            },
2208            Box<[u8]> {
2209                CharacterLengths,
2210                WordLengths
2211            },
2212            Box<[f32]> {
2213                CharacterPositions,
2214                CharacterWidths
2215            },
2216            bool {
2217                Expanded,
2218                Selected
2219            },
2220            Invalid { Invalid },
2221            Toggled { Toggled },
2222            Live { Live },
2223            TextDirection { TextDirection },
2224            Orientation { Orientation },
2225            SortDirection { SortDirection },
2226            AriaCurrent { AriaCurrent },
2227            AutoComplete { AutoComplete },
2228            HasPopup { HasPopup },
2229            ListStyle { ListStyle },
2230            TextAlign { TextAlign },
2231            VerticalOffset { VerticalOffset },
2232            Affine { Transform },
2233            Rect { Bounds },
2234            TextSelection { TextSelection },
2235            Vec<CustomAction> { CustomActions }
2236        });
2237        SchemaObject {
2238            instance_type: Some(InstanceType::Object.into()),
2239            object: Some(
2240                ObjectValidation {
2241                    properties,
2242                    ..Default::default()
2243                }
2244                .into(),
2245            ),
2246            ..Default::default()
2247        }
2248        .into()
2249    }
2250}
2251
2252/// The data associated with an accessibility tree that's global to the
2253/// tree and not associated with any particular node.
2254#[derive(Clone, Debug, PartialEq, Eq)]
2255#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2256#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2257#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2258#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2259pub struct Tree {
2260    /// The identifier of the tree's root node.
2261    pub root: NodeId,
2262    /// The name of the UI toolkit in use.
2263    pub toolkit_name: Option<String>,
2264    /// The version of the UI toolkit.
2265    pub toolkit_version: Option<String>,
2266}
2267
2268impl Tree {
2269    #[inline]
2270    pub fn new(root: NodeId) -> Tree {
2271        Tree {
2272            root,
2273            toolkit_name: None,
2274            toolkit_version: None,
2275        }
2276    }
2277}
2278
2279/// A serializable representation of an atomic change to a [`Tree`].
2280///
2281/// The sender and receiver must be in sync; the update is only meant
2282/// to bring the tree from a specific previous state into its next state.
2283/// Trying to apply it to the wrong tree should immediately panic.
2284///
2285/// Note that for performance, an update should only include nodes that are
2286/// new or changed. AccessKit platform adapters will avoid raising extraneous
2287/// events for nodes that have not changed since the previous update,
2288/// but there is still a cost in processing these nodes and replacing
2289/// the previous instances.
2290#[derive(Clone, Debug, PartialEq)]
2291#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2292#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2293#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2294#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2295pub struct TreeUpdate {
2296    /// Zero or more new or updated nodes. Order doesn't matter.
2297    ///
2298    /// Each node in this list will overwrite any existing node with the same ID.
2299    /// This means that when updating a node, fields that are unchanged
2300    /// from the previous version must still be set to the same values
2301    /// as before.
2302    ///
2303    /// It is an error for any node in this list to not be either the root
2304    /// or a child of another node. For nodes other than the root, the parent
2305    /// must be either an unchanged node already in the tree, or another node
2306    /// in this list.
2307    ///
2308    /// To add a child to the tree, the list must include both the child
2309    /// and an updated version of the parent with the child's ID added to
2310    /// [`Node::children`].
2311    ///
2312    /// To remove a child and all of its descendants, this list must include
2313    /// an updated version of the parent node with the child's ID removed
2314    /// from [`Node::children`]. Neither the child nor any of its descendants
2315    /// may be included in this list.
2316    pub nodes: Vec<(NodeId, Node)>,
2317
2318    /// Rarely updated information about the tree as a whole. This may be omitted
2319    /// if it has not changed since the previous update, but providing the same
2320    /// information again is also allowed. This is required when initializing
2321    /// a tree.
2322    pub tree: Option<Tree>,
2323
2324    /// The node within this tree that has keyboard focus when the native
2325    /// host (e.g. window) has focus. If no specific node within the tree
2326    /// has keyboard focus, this must be set to the root. The latest focus state
2327    /// must be provided with every tree update, even if the focus state
2328    /// didn't change in a given update.
2329    pub focus: NodeId,
2330}
2331
2332#[derive(Clone, Debug, PartialEq)]
2333#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2334#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2335#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2336#[repr(C)]
2337pub enum ActionData {
2338    CustomAction(i32),
2339    Value(Box<str>),
2340    NumericValue(f64),
2341    /// Optional target rectangle for [`Action::ScrollIntoView`], in
2342    /// the coordinate space of the action's target node.
2343    ScrollTargetRect(Rect),
2344    /// Target for [`Action::ScrollToPoint`], in platform-native coordinates
2345    /// relative to the origin of the tree's container (e.g. window).
2346    ScrollToPoint(Point),
2347    /// Target for [`Action::SetScrollOffset`], in the coordinate space
2348    /// of the action's target node.
2349    SetScrollOffset(Point),
2350    SetTextSelection(TextSelection),
2351}
2352
2353#[derive(Clone, Debug, PartialEq)]
2354#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2355#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2356#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2357#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2358pub struct ActionRequest {
2359    pub action: Action,
2360    pub target: NodeId,
2361    pub data: Option<ActionData>,
2362}
2363
2364/// Handles activation of the application's accessibility implementation.
2365pub trait ActivationHandler {
2366    /// Requests a [`TreeUpdate`] with a full tree. If the application
2367    /// can generate the tree synchronously within this method call,
2368    /// it should do so and return the [`TreeUpdate`]. Otherwise,
2369    /// it must send the update to the platform adapter asynchronously,
2370    /// no later than the next display refresh, even if a frame would not
2371    /// normally be rendered due to user input or other activity.
2372    /// The application should not return or send a placeholder [`TreeUpdate`];
2373    /// the platform adapter will provide one if necessary until the real
2374    /// tree is sent.
2375    ///
2376    /// The primary purpose of this method is to allow the application
2377    /// to lazily initialize its accessibility implementation. However,
2378    /// this method may be called consecutively without any call to
2379    /// [`DeactivationHandler::deactivate_accessibility`]; this typically happens
2380    /// if the platform adapter merely forwards tree updates to assistive
2381    /// technologies without maintaining any state. A call to this method
2382    /// must always generate a [`TreeUpdate`] with a full tree, even if
2383    /// the application normally sends incremental updates.
2384    ///
2385    /// The thread on which this method is called is platform-dependent.
2386    /// Refer to the platform adapter documentation for more details.
2387    fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
2388}
2389
2390/// Handles requests from assistive technologies or other clients.
2391pub trait ActionHandler {
2392    /// Perform the requested action. If the requested action is not supported,
2393    /// this method must do nothing.
2394    ///
2395    /// The thread on which this method is called is platform-dependent.
2396    /// Refer to the platform adapter documentation for more details.
2397    ///
2398    /// This method may queue the request and handle it asynchronously.
2399    /// This behavior is preferred over blocking, e.g. when dispatching
2400    /// the request to another thread.
2401    fn do_action(&mut self, request: ActionRequest);
2402}
2403
2404/// Handles deactivation of the application's accessibility implementation.
2405pub trait DeactivationHandler {
2406    /// Deactivate the application's accessibility implementation and drop any
2407    /// associated data that can be reconstructed later. After this method
2408    /// is called, if an accessibility tree is needed again, the platform
2409    /// adapter will call [`ActivationHandler::request_initial_tree`] again.
2410    ///
2411    /// The thread on which this method is called is platform-dependent.
2412    /// Refer to the platform adapter documentation for more details.
2413    fn deactivate_accessibility(&mut self);
2414}
2415
2416#[cfg(test)]
2417mod tests {
2418    use super::*;
2419
2420    #[test]
2421    fn action_n() {
2422        assert_eq!(Action::n(0), Some(Action::Click));
2423        assert_eq!(Action::n(1), Some(Action::Focus));
2424        assert_eq!(Action::n(2), Some(Action::Blur));
2425        assert_eq!(Action::n(3), Some(Action::Collapse));
2426        assert_eq!(Action::n(4), Some(Action::Expand));
2427        assert_eq!(Action::n(5), Some(Action::CustomAction));
2428        assert_eq!(Action::n(6), Some(Action::Decrement));
2429        assert_eq!(Action::n(7), Some(Action::Increment));
2430        assert_eq!(Action::n(8), Some(Action::HideTooltip));
2431        assert_eq!(Action::n(9), Some(Action::ShowTooltip));
2432        assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
2433        assert_eq!(Action::n(11), Some(Action::ScrollBackward));
2434        assert_eq!(Action::n(12), Some(Action::ScrollDown));
2435        assert_eq!(Action::n(13), Some(Action::ScrollForward));
2436        assert_eq!(Action::n(14), Some(Action::ScrollLeft));
2437        assert_eq!(Action::n(15), Some(Action::ScrollRight));
2438        assert_eq!(Action::n(16), Some(Action::ScrollUp));
2439        assert_eq!(Action::n(17), Some(Action::ScrollIntoView));
2440        assert_eq!(Action::n(18), Some(Action::ScrollToPoint));
2441        assert_eq!(Action::n(19), Some(Action::SetScrollOffset));
2442        assert_eq!(Action::n(20), Some(Action::SetTextSelection));
2443        assert_eq!(
2444            Action::n(21),
2445            Some(Action::SetSequentialFocusNavigationStartingPoint)
2446        );
2447        assert_eq!(Action::n(22), Some(Action::SetValue));
2448        assert_eq!(Action::n(23), Some(Action::ShowContextMenu));
2449        assert_eq!(Action::n(24), None);
2450    }
2451
2452    #[test]
2453    fn test_action_mask_to_action_vec() {
2454        assert_eq!(
2455            Vec::<Action>::new(),
2456            action_mask_to_action_vec(Node::new(Role::Unknown).actions)
2457        );
2458
2459        let mut node = Node::new(Role::Unknown);
2460        node.add_action(Action::Click);
2461        assert_eq!(
2462            &[Action::Click],
2463            action_mask_to_action_vec(node.actions).as_slice()
2464        );
2465
2466        let mut node = Node::new(Role::Unknown);
2467        node.add_action(Action::ShowContextMenu);
2468        assert_eq!(
2469            &[Action::ShowContextMenu],
2470            action_mask_to_action_vec(node.actions).as_slice()
2471        );
2472
2473        let mut node = Node::new(Role::Unknown);
2474        node.add_action(Action::Click);
2475        node.add_action(Action::ShowContextMenu);
2476        assert_eq!(
2477            &[Action::Click, Action::ShowContextMenu],
2478            action_mask_to_action_vec(node.actions).as_slice()
2479        );
2480
2481        let mut node = Node::new(Role::Unknown);
2482        node.add_action(Action::Focus);
2483        node.add_action(Action::Blur);
2484        node.add_action(Action::Collapse);
2485        assert_eq!(
2486            &[Action::Focus, Action::Blur, Action::Collapse],
2487            action_mask_to_action_vec(node.actions).as_slice()
2488        );
2489    }
2490}