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