Skip to main content

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
15#[cfg(feature = "schemars")]
16use alloc::borrow::Cow;
17use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
18use core::fmt;
19#[cfg(feature = "pyo3")]
20use pyo3::pyclass;
21#[cfg(feature = "schemars")]
22use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
23#[cfg(feature = "serde")]
24use serde::{
25    Deserialize, Serialize,
26    de::{Deserializer, IgnoredAny, MapAccess, Visitor},
27    ser::{SerializeMap, Serializer},
28};
29#[cfg(feature = "schemars")]
30use serde_json::{Map as SchemaMap, Value as SchemaValue};
31
32pub use uuid::Uuid;
33
34mod geometry;
35pub use geometry::{Affine, Point, Rect, Size, Vec2};
36
37/// The type of an accessibility node.
38///
39/// The majority of these roles come from the ARIA specification. Reference
40/// the latest draft for proper usage.
41///
42/// Like the AccessKit schema as a whole, this list is largely taken
43/// from Chromium. However, unlike Chromium's alphabetized list, this list
44/// is ordered roughly by expected usage frequency (with the notable exception
45/// of [`Role::Unknown`]). This is more efficient in serialization formats
46/// where integers use a variable-length encoding.
47#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(feature = "enumn", derive(enumn::N))]
49#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
50#[cfg_attr(feature = "schemars", derive(JsonSchema))]
51#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
52#[cfg_attr(
53    feature = "pyo3",
54    pyclass(
55        module = "accesskit",
56        rename_all = "SCREAMING_SNAKE_CASE",
57        eq,
58        from_py_object
59    )
60)]
61#[repr(u8)]
62pub enum Role {
63    #[default]
64    Unknown,
65    TextRun,
66    Cell,
67    Label,
68    Image,
69    Link,
70    Row,
71    ListItem,
72
73    /// Contains the bullet, number, or other marker for a list item.
74    ListMarker,
75
76    TreeItem,
77    ListBoxOption,
78    MenuItem,
79    MenuListOption,
80    Paragraph,
81
82    /// A generic container that should be ignored by assistive technologies
83    /// and filtered out of platform accessibility trees. Equivalent to the ARIA
84    /// `none` or `presentation` role, or to an HTML `div` with no role.
85    GenericContainer,
86
87    CheckBox,
88    RadioButton,
89    TextInput,
90    Button,
91    DefaultButton,
92    Pane,
93    RowHeader,
94    ColumnHeader,
95    RowGroup,
96    List,
97    Table,
98    LayoutTableCell,
99    LayoutTableRow,
100    LayoutTable,
101    Switch,
102    Menu,
103
104    MultilineTextInput,
105    SearchInput,
106    DateInput,
107    DateTimeInput,
108    WeekInput,
109    MonthInput,
110    TimeInput,
111    EmailInput,
112    NumberInput,
113    PasswordInput,
114    PhoneNumberInput,
115    UrlInput,
116
117    Abbr,
118    Alert,
119    AlertDialog,
120    Application,
121    Article,
122    Audio,
123    Banner,
124    Blockquote,
125    Canvas,
126    Caption,
127    Caret,
128    Code,
129    ColorWell,
130    ComboBox,
131    EditableComboBox,
132    Complementary,
133    Comment,
134    ContentDeletion,
135    ContentInsertion,
136    ContentInfo,
137    Definition,
138    DescriptionList,
139    Details,
140    Dialog,
141    DisclosureTriangle,
142    Document,
143    EmbeddedObject,
144    Emphasis,
145    Feed,
146    FigureCaption,
147    Figure,
148    Footer,
149    Form,
150    Grid,
151    GridCell,
152    Group,
153    Header,
154    Heading,
155    Iframe,
156    IframePresentational,
157    ImeCandidate,
158    Keyboard,
159    Legend,
160    LineBreak,
161    ListBox,
162    Log,
163    Main,
164    Mark,
165    Marquee,
166    Math,
167    MenuBar,
168    MenuItemCheckBox,
169    MenuItemRadio,
170    MenuListPopup,
171    Meter,
172    Navigation,
173    Note,
174    PluginObject,
175    ProgressIndicator,
176    RadioGroup,
177    Region,
178    RootWebArea,
179    Ruby,
180    RubyAnnotation,
181    ScrollBar,
182    ScrollView,
183    Search,
184    Section,
185    SectionFooter,
186    SectionHeader,
187    Slider,
188    SpinButton,
189    Splitter,
190    Status,
191    Strong,
192    Suggestion,
193    SvgRoot,
194    Tab,
195    TabList,
196    TabPanel,
197    Term,
198    Time,
199    Timer,
200    TitleBar,
201    Toolbar,
202    Tooltip,
203    Tree,
204    TreeGrid,
205    Video,
206    WebView,
207    Window,
208
209    PdfActionableHighlight,
210    PdfRoot,
211
212    // ARIA Graphics module roles:
213    // https://rawgit.com/w3c/graphics-aam/master/#mapping_role_table
214    GraphicsDocument,
215    GraphicsObject,
216    GraphicsSymbol,
217
218    // DPub Roles:
219    // https://www.w3.org/TR/dpub-aam-1.0/#mapping_role_table
220    DocAbstract,
221    DocAcknowledgements,
222    DocAfterword,
223    DocAppendix,
224    DocBackLink,
225    DocBiblioEntry,
226    DocBibliography,
227    DocBiblioRef,
228    DocChapter,
229    DocColophon,
230    DocConclusion,
231    DocCover,
232    DocCredit,
233    DocCredits,
234    DocDedication,
235    DocEndnote,
236    DocEndnotes,
237    DocEpigraph,
238    DocEpilogue,
239    DocErrata,
240    DocExample,
241    DocFootnote,
242    DocForeword,
243    DocGlossary,
244    DocGlossRef,
245    DocIndex,
246    DocIntroduction,
247    DocNoteRef,
248    DocNotice,
249    DocPageBreak,
250    DocPageFooter,
251    DocPageHeader,
252    DocPageList,
253    DocPart,
254    DocPreface,
255    DocPrologue,
256    DocPullquote,
257    DocQna,
258    DocSubtitle,
259    DocTip,
260    DocToc,
261
262    /// Behaves similar to an ARIA grid but is primarily used by Chromium's
263    /// `TableView` and its subclasses, so they can be exposed correctly
264    /// on certain platforms.
265    ListGrid,
266
267    /// This is just like a multi-line document, but signals that assistive
268    /// technologies should implement behavior specific to a VT-100-style
269    /// terminal.
270    Terminal,
271}
272
273/// An action to be taken on an accessibility node.
274#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
275#[cfg_attr(feature = "enumn", derive(enumn::N))]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[cfg_attr(feature = "schemars", derive(JsonSchema))]
278#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
279#[cfg_attr(
280    feature = "pyo3",
281    pyclass(
282        module = "accesskit",
283        rename_all = "SCREAMING_SNAKE_CASE",
284        eq,
285        from_py_object
286    )
287)]
288#[repr(u8)]
289pub enum Action {
290    /// Do the equivalent of a single click or tap.
291    Click,
292
293    Focus,
294    Blur,
295
296    Collapse,
297    Expand,
298
299    /// Requires [`ActionRequest::data`] to be set to [`ActionData::CustomAction`].
300    CustomAction,
301
302    /// Decrement a numeric value by one step.
303    Decrement,
304    /// Increment a numeric value by one step.
305    Increment,
306
307    HideTooltip,
308    ShowTooltip,
309
310    /// Delete any selected text in the control's text value and
311    /// insert the specified value in its place, like when typing or pasting.
312    /// Requires [`ActionRequest::data`] to be set to [`ActionData::Value`].
313    ReplaceSelectedText,
314
315    /// Scroll down by the specified unit.
316    ScrollDown,
317    /// Scroll left by the specified unit.
318    ScrollLeft,
319    /// Scroll right by the specified unit.
320    ScrollRight,
321    /// Scroll up by the specified unit.
322    ScrollUp,
323
324    /// Scroll any scrollable containers to make the target node visible.
325    /// Optionally set [`ActionRequest::data`] to [`ActionData::ScrollHint`].
326    ScrollIntoView,
327
328    /// Scroll the given object to a specified point in the tree's container
329    /// (e.g. window). Requires [`ActionRequest::data`] to be set to
330    /// [`ActionData::ScrollToPoint`].
331    ScrollToPoint,
332
333    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetScrollOffset`].
334    SetScrollOffset,
335
336    /// Requires [`ActionRequest::data`] to be set to [`ActionData::SetTextSelection`].
337    SetTextSelection,
338
339    /// Don't focus this node, but set it as the sequential focus navigation
340    /// starting point, so that pressing Tab moves to the next element
341    /// following this one, for example.
342    SetSequentialFocusNavigationStartingPoint,
343
344    /// Replace the value of the control with the specified value and
345    /// reset the selection, if applicable. Requires [`ActionRequest::data`]
346    /// to be set to [`ActionData::Value`] or [`ActionData::NumericValue`].
347    SetValue,
348
349    ShowContextMenu,
350}
351
352impl Action {
353    fn mask(self) -> u32 {
354        1 << (self as u8)
355    }
356
357    #[cfg(not(feature = "enumn"))]
358    fn n(value: u8) -> Option<Self> {
359        // Manually implement something similar to the enumn crate. We don't
360        // want to bring this crate by default though and we can't use a
361        // macro as it would break C bindings header file generation.
362        match value {
363            0 => Some(Action::Click),
364            1 => Some(Action::Focus),
365            2 => Some(Action::Blur),
366            3 => Some(Action::Collapse),
367            4 => Some(Action::Expand),
368            5 => Some(Action::CustomAction),
369            6 => Some(Action::Decrement),
370            7 => Some(Action::Increment),
371            8 => Some(Action::HideTooltip),
372            9 => Some(Action::ShowTooltip),
373            10 => Some(Action::ReplaceSelectedText),
374            11 => Some(Action::ScrollDown),
375            12 => Some(Action::ScrollLeft),
376            13 => Some(Action::ScrollRight),
377            14 => Some(Action::ScrollUp),
378            15 => Some(Action::ScrollIntoView),
379            16 => Some(Action::ScrollToPoint),
380            17 => Some(Action::SetScrollOffset),
381            18 => Some(Action::SetTextSelection),
382            19 => Some(Action::SetSequentialFocusNavigationStartingPoint),
383            20 => Some(Action::SetValue),
384            21 => Some(Action::ShowContextMenu),
385            _ => None,
386        }
387    }
388}
389
390fn action_mask_to_action_vec(mask: u32) -> Vec<Action> {
391    let mut actions = Vec::new();
392    let mut i = 0;
393    while let Some(variant) = Action::n(i) {
394        if mask & variant.mask() != 0 {
395            actions.push(variant);
396        }
397        i += 1;
398    }
399    actions
400}
401
402#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
403#[cfg_attr(feature = "enumn", derive(enumn::N))]
404#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
405#[cfg_attr(feature = "schemars", derive(JsonSchema))]
406#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
407#[cfg_attr(
408    feature = "pyo3",
409    pyclass(
410        module = "accesskit",
411        rename_all = "SCREAMING_SNAKE_CASE",
412        eq,
413        from_py_object
414    )
415)]
416#[repr(u8)]
417pub enum Orientation {
418    /// E.g. most toolbars and separators.
419    Horizontal,
420    /// E.g. menu or combo box.
421    Vertical,
422}
423
424#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
425#[cfg_attr(feature = "enumn", derive(enumn::N))]
426#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
427#[cfg_attr(feature = "schemars", derive(JsonSchema))]
428#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
429#[cfg_attr(
430    feature = "pyo3",
431    pyclass(
432        module = "accesskit",
433        rename_all = "SCREAMING_SNAKE_CASE",
434        eq,
435        from_py_object
436    )
437)]
438#[repr(u8)]
439pub enum TextDirection {
440    LeftToRight,
441    RightToLeft,
442    TopToBottom,
443    BottomToTop,
444}
445
446/// Indicates if a form control has invalid input or if a web DOM element has an
447/// [`aria-invalid`] attribute.
448///
449/// [`aria-invalid`]: https://www.w3.org/TR/wai-aria-1.1/#aria-invalid
450#[derive(Clone, Copy, Debug, PartialEq, Eq)]
451#[cfg_attr(feature = "enumn", derive(enumn::N))]
452#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
453#[cfg_attr(feature = "schemars", derive(JsonSchema))]
454#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
455#[cfg_attr(
456    feature = "pyo3",
457    pyclass(
458        module = "accesskit",
459        rename_all = "SCREAMING_SNAKE_CASE",
460        eq,
461        from_py_object
462    )
463)]
464#[repr(u8)]
465pub enum Invalid {
466    True,
467    Grammar,
468    Spelling,
469}
470
471#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
472#[cfg_attr(feature = "enumn", derive(enumn::N))]
473#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
474#[cfg_attr(feature = "schemars", derive(JsonSchema))]
475#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
476#[cfg_attr(
477    feature = "pyo3",
478    pyclass(
479        module = "accesskit",
480        rename_all = "SCREAMING_SNAKE_CASE",
481        eq,
482        from_py_object
483    )
484)]
485#[repr(u8)]
486pub enum Toggled {
487    False,
488    True,
489    Mixed,
490}
491
492impl From<bool> for Toggled {
493    #[inline]
494    fn from(b: bool) -> Self {
495        match b {
496            false => Self::False,
497            true => Self::True,
498        }
499    }
500}
501
502#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
503#[cfg_attr(feature = "enumn", derive(enumn::N))]
504#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
505#[cfg_attr(feature = "schemars", derive(JsonSchema))]
506#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
507#[cfg_attr(
508    feature = "pyo3",
509    pyclass(
510        module = "accesskit",
511        rename_all = "SCREAMING_SNAKE_CASE",
512        eq,
513        from_py_object
514    )
515)]
516#[repr(u8)]
517pub enum SortDirection {
518    Ascending,
519    Descending,
520    Other,
521}
522
523#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
524#[cfg_attr(feature = "enumn", derive(enumn::N))]
525#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
526#[cfg_attr(feature = "schemars", derive(JsonSchema))]
527#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
528#[cfg_attr(
529    feature = "pyo3",
530    pyclass(
531        module = "accesskit",
532        rename_all = "SCREAMING_SNAKE_CASE",
533        eq,
534        from_py_object
535    )
536)]
537#[repr(u8)]
538pub enum AriaCurrent {
539    False,
540    True,
541    Page,
542    Step,
543    Location,
544    Date,
545    Time,
546}
547
548#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
549#[cfg_attr(feature = "enumn", derive(enumn::N))]
550#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
551#[cfg_attr(feature = "schemars", derive(JsonSchema))]
552#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
553#[cfg_attr(
554    feature = "pyo3",
555    pyclass(
556        module = "accesskit",
557        rename_all = "SCREAMING_SNAKE_CASE",
558        eq,
559        from_py_object
560    )
561)]
562#[repr(u8)]
563pub enum AutoComplete {
564    Inline,
565    List,
566    Both,
567}
568
569#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
570#[cfg_attr(feature = "enumn", derive(enumn::N))]
571#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
572#[cfg_attr(feature = "schemars", derive(JsonSchema))]
573#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
574#[cfg_attr(
575    feature = "pyo3",
576    pyclass(
577        module = "accesskit",
578        rename_all = "SCREAMING_SNAKE_CASE",
579        eq,
580        from_py_object
581    )
582)]
583#[repr(u8)]
584pub enum Live {
585    Off,
586    Polite,
587    Assertive,
588}
589
590#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
591#[cfg_attr(feature = "enumn", derive(enumn::N))]
592#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
593#[cfg_attr(feature = "schemars", derive(JsonSchema))]
594#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
595#[cfg_attr(
596    feature = "pyo3",
597    pyclass(
598        module = "accesskit",
599        rename_all = "SCREAMING_SNAKE_CASE",
600        eq,
601        from_py_object
602    )
603)]
604#[repr(u8)]
605pub enum HasPopup {
606    Menu,
607    Listbox,
608    Tree,
609    Grid,
610    Dialog,
611}
612
613#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
614#[cfg_attr(feature = "enumn", derive(enumn::N))]
615#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
616#[cfg_attr(feature = "schemars", derive(JsonSchema))]
617#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
618#[cfg_attr(
619    feature = "pyo3",
620    pyclass(
621        module = "accesskit",
622        rename_all = "SCREAMING_SNAKE_CASE",
623        eq,
624        from_py_object
625    )
626)]
627#[repr(u8)]
628pub enum ListStyle {
629    Circle,
630    Disc,
631    Image,
632    Numeric,
633    Square,
634    /// Language specific ordering (alpha, roman, cjk-ideographic, etc...)
635    Other,
636}
637
638#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
639#[cfg_attr(feature = "enumn", derive(enumn::N))]
640#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
641#[cfg_attr(feature = "schemars", derive(JsonSchema))]
642#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
643#[cfg_attr(
644    feature = "pyo3",
645    pyclass(
646        module = "accesskit",
647        rename_all = "SCREAMING_SNAKE_CASE",
648        eq,
649        from_py_object
650    )
651)]
652#[repr(u8)]
653pub enum TextAlign {
654    Left,
655    Right,
656    Center,
657    Justify,
658}
659
660#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
661#[cfg_attr(feature = "enumn", derive(enumn::N))]
662#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
663#[cfg_attr(feature = "schemars", derive(JsonSchema))]
664#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
665#[cfg_attr(
666    feature = "pyo3",
667    pyclass(
668        module = "accesskit",
669        rename_all = "SCREAMING_SNAKE_CASE",
670        eq,
671        from_py_object
672    )
673)]
674#[repr(u8)]
675pub enum VerticalOffset {
676    Subscript,
677    Superscript,
678}
679
680#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
681#[cfg_attr(feature = "enumn", derive(enumn::N))]
682#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
683#[cfg_attr(feature = "schemars", derive(JsonSchema))]
684#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
685#[cfg_attr(
686    feature = "pyo3",
687    pyclass(
688        module = "accesskit",
689        rename_all = "SCREAMING_SNAKE_CASE",
690        eq,
691        from_py_object
692    )
693)]
694#[repr(u8)]
695pub enum TextDecorationStyle {
696    Solid,
697    Dotted,
698    Dashed,
699    Double,
700    Wavy,
701}
702
703pub type NodeIdContent = u64;
704
705/// The stable identity of a [`Node`], unique within the node's tree.
706///
707/// Each tree (root or subtree) has its own independent ID space. The same
708/// `NodeId` value can exist in different trees without conflict. When working
709/// with multiple trees, the combination of `NodeId` and [`TreeId`] uniquely
710/// identifies a node.
711#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
712#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
713#[cfg_attr(feature = "schemars", derive(JsonSchema))]
714#[repr(transparent)]
715pub struct NodeId(pub NodeIdContent);
716
717impl From<NodeIdContent> for NodeId {
718    #[inline]
719    fn from(inner: NodeIdContent) -> Self {
720        Self(inner)
721    }
722}
723
724impl From<NodeId> for NodeIdContent {
725    #[inline]
726    fn from(outer: NodeId) -> Self {
727        outer.0
728    }
729}
730
731impl fmt::Debug for NodeId {
732    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
733        write!(f, "#{}", self.0)
734    }
735}
736
737/// The stable identity of a tree.
738///
739/// Use [`TreeId::ROOT`] for the main/root tree. For subtrees, use a random
740/// UUID (version 4) to avoid collisions between independently created trees.
741#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
742#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
743#[cfg_attr(feature = "schemars", derive(JsonSchema))]
744#[repr(transparent)]
745pub struct TreeId(pub Uuid);
746
747impl TreeId {
748    /// A reserved tree ID for the root tree. This uses a nil UUID.
749    pub const ROOT: Self = Self(Uuid::nil());
750}
751
752/// Defines a custom action for a UI element.
753///
754/// For example, a list UI can allow a user to reorder items in the list by dragging the
755/// items.
756#[derive(Clone, Debug, PartialEq, Eq)]
757#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
758#[cfg_attr(feature = "schemars", derive(JsonSchema))]
759#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
760#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
761pub struct CustomAction {
762    pub id: i32,
763    pub description: String,
764}
765
766#[derive(Clone, Copy, Debug, PartialEq, Eq)]
767#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
768#[cfg_attr(feature = "schemars", derive(JsonSchema))]
769#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
770#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
771pub struct TextPosition {
772    /// The node's role must be [`Role::TextRun`].
773    pub node: NodeId,
774    /// The index of an item in [`Node::character_lengths`], or the length
775    /// of that slice if the position is at the end of the line.
776    pub character_index: usize,
777}
778
779#[derive(Clone, Copy, Debug, PartialEq, Eq)]
780#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
781#[cfg_attr(feature = "schemars", derive(JsonSchema))]
782#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
783#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
784pub struct TextSelection {
785    /// The position where the selection started, and which does not change
786    /// as the selection is expanded or contracted. If there is no selection
787    /// but only a caret, this must be equal to the value of [`TextSelection::focus`].
788    /// This is also known as a degenerate selection.
789    pub anchor: TextPosition,
790    /// The active end of the selection, which changes as the selection
791    /// is expanded or contracted, or the position of the caret if there is
792    /// no selection.
793    pub focus: TextPosition,
794}
795
796#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
797#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
798#[cfg_attr(feature = "schemars", derive(JsonSchema))]
799#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
800#[repr(u8)]
801enum Flag {
802    Hidden,
803    Multiselectable,
804    Required,
805    Visited,
806    Busy,
807    LiveAtomic,
808    Modal,
809    TouchTransparent,
810    ReadOnly,
811    Disabled,
812    Italic,
813    ClipsChildren,
814    IsLineBreakingObject,
815    IsPageBreakingObject,
816    IsSpellingError,
817    IsGrammarError,
818    IsSearchMatch,
819    IsSuggestion,
820}
821
822impl Flag {
823    fn mask(self) -> u32 {
824        1 << (self as u8)
825    }
826}
827
828/// A color represented in 8-bit sRGB plus alpha.
829#[derive(Clone, Copy, Debug, PartialEq, Eq)]
830#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
831#[cfg_attr(feature = "schemars", derive(JsonSchema))]
832#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
833#[repr(C)]
834pub struct Color {
835    pub red: u8,
836    pub green: u8,
837    pub blue: u8,
838    pub alpha: u8,
839}
840
841/// The style and color for a type of text decoration.
842#[derive(Clone, Copy, Debug, PartialEq, Eq)]
843#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
844#[cfg_attr(feature = "schemars", derive(JsonSchema))]
845#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
846#[repr(C)]
847pub struct TextDecoration {
848    pub style: TextDecorationStyle,
849    pub color: Color,
850}
851
852// The following is based on the technique described here:
853// https://viruta.org/reducing-memory-consumption-in-librsvg-2.html
854
855#[derive(Debug, PartialEq)]
856enum PropertyValue {
857    None,
858    NodeIdVec(Vec<NodeId>),
859    NodeId(NodeId),
860    String(String),
861    F64(f64),
862    F32(f32),
863    Usize(usize),
864    Color(Color),
865    TextDecoration(TextDecoration),
866    LengthVec(Vec<u8>),
867    CoordVec(Vec<f32>),
868    Bool(bool),
869    Invalid(Invalid),
870    Toggled(Toggled),
871    Live(Live),
872    TextDirection(TextDirection),
873    Orientation(Orientation),
874    SortDirection(SortDirection),
875    AriaCurrent(AriaCurrent),
876    AutoComplete(AutoComplete),
877    HasPopup(HasPopup),
878    ListStyle(ListStyle),
879    TextAlign(TextAlign),
880    VerticalOffset(VerticalOffset),
881    Affine(Box<Affine>),
882    Rect(Rect),
883    TextSelection(Box<TextSelection>),
884    CustomActionVec(Vec<CustomAction>),
885    TreeId(TreeId),
886}
887
888#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
889#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
890#[cfg_attr(feature = "schemars", derive(JsonSchema))]
891#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
892#[repr(u8)]
893enum PropertyId {
894    // NodeIdVec
895    Children,
896    Controls,
897    Details,
898    DescribedBy,
899    FlowTo,
900    LabelledBy,
901    Owns,
902    RadioGroup,
903
904    // NodeId
905    ActiveDescendant,
906    ErrorMessage,
907    InPageLinkTarget,
908    MemberOf,
909    NextOnLine,
910    PreviousOnLine,
911    PopupFor,
912
913    // String
914    Label,
915    Description,
916    Value,
917    AccessKey,
918    AuthorId,
919    ClassName,
920    FontFamily,
921    HtmlId,
922    HtmlTag,
923    InnerHtml,
924    KeyboardShortcut,
925    Language,
926    Placeholder,
927    RoleDescription,
928    StateDescription,
929    Tooltip,
930    Url,
931    RowIndexText,
932    ColumnIndexText,
933    BrailleLabel,
934    BrailleRoleDescription,
935
936    // f64
937    ScrollX,
938    ScrollXMin,
939    ScrollXMax,
940    ScrollY,
941    ScrollYMin,
942    ScrollYMax,
943    NumericValue,
944    MinNumericValue,
945    MaxNumericValue,
946    NumericValueStep,
947    NumericValueJump,
948
949    // f32
950    FontSize,
951    FontWeight,
952
953    // usize
954    RowCount,
955    ColumnCount,
956    RowIndex,
957    ColumnIndex,
958    RowSpan,
959    ColumnSpan,
960    Level,
961    SizeOfSet,
962    PositionInSet,
963
964    // Color
965    ColorValue,
966    BackgroundColor,
967    ForegroundColor,
968
969    // TextDecoration
970    Overline,
971    Strikethrough,
972    Underline,
973
974    // LengthVec
975    CharacterLengths,
976    WordStarts,
977
978    // CoordVec
979    CharacterPositions,
980    CharacterWidths,
981
982    // bool
983    Expanded,
984    Selected,
985
986    // Unique enums
987    Invalid,
988    Toggled,
989    Live,
990    TextDirection,
991    Orientation,
992    SortDirection,
993    AriaCurrent,
994    AutoComplete,
995    HasPopup,
996    ListStyle,
997    TextAlign,
998    VerticalOffset,
999
1000    // Other
1001    Transform,
1002    Bounds,
1003    TextSelection,
1004    CustomActions,
1005    TreeId,
1006
1007    // This MUST be last.
1008    Unset,
1009}
1010
1011impl Clone for PropertyValue {
1012    fn clone(&self) -> Self {
1013        match self {
1014            Self::None => Self::None,
1015            Self::NodeIdVec(v) => Self::NodeIdVec(v.clone()),
1016            Self::NodeId(v) => Self::NodeId(*v),
1017            Self::String(v) => Self::String(v.clone()),
1018            Self::F64(v) => Self::F64(*v),
1019            Self::F32(v) => Self::F32(*v),
1020            Self::Usize(v) => Self::Usize(*v),
1021            Self::Color(v) => Self::Color(*v),
1022            Self::TextDecoration(v) => Self::TextDecoration(*v),
1023            Self::LengthVec(v) => Self::LengthVec(v.clone()),
1024            Self::CoordVec(v) => Self::CoordVec(v.clone()),
1025            Self::Bool(v) => Self::Bool(*v),
1026            Self::Invalid(v) => Self::Invalid(*v),
1027            Self::Toggled(v) => Self::Toggled(*v),
1028            Self::Live(v) => Self::Live(*v),
1029            Self::TextDirection(v) => Self::TextDirection(*v),
1030            Self::Orientation(v) => Self::Orientation(*v),
1031            Self::SortDirection(v) => Self::SortDirection(*v),
1032            Self::AriaCurrent(v) => Self::AriaCurrent(*v),
1033            Self::AutoComplete(v) => Self::AutoComplete(*v),
1034            Self::HasPopup(v) => Self::HasPopup(*v),
1035            Self::ListStyle(v) => Self::ListStyle(*v),
1036            Self::TextAlign(v) => Self::TextAlign(*v),
1037            Self::VerticalOffset(v) => Self::VerticalOffset(*v),
1038            Self::Affine(v) => Self::Affine(v.clone()),
1039            Self::Rect(v) => Self::Rect(*v),
1040            Self::TextSelection(v) => Self::TextSelection(v.clone()),
1041            Self::CustomActionVec(v) => Self::CustomActionVec(v.clone()),
1042            Self::TreeId(v) => Self::TreeId(*v),
1043        }
1044    }
1045
1046    fn clone_from(&mut self, source: &Self) {
1047        match (self, source) {
1048            (Self::NodeIdVec(dest), Self::NodeIdVec(source)) => dest.clone_from(source),
1049            (Self::String(dest), Self::String(source)) => dest.clone_from(source),
1050            (Self::LengthVec(dest), Self::LengthVec(source)) => dest.clone_from(source),
1051            (Self::CoordVec(dest), Self::CoordVec(source)) => dest.clone_from(source),
1052            (Self::CustomActionVec(dest), Self::CustomActionVec(source)) => dest.clone_from(source),
1053            (Self::Affine(dest), Self::Affine(source)) => dest.clone_from(source),
1054            (Self::TextSelection(dest), Self::TextSelection(source)) => dest.clone_from(source),
1055            (dest, source) => *dest = source.clone(),
1056        }
1057    }
1058}
1059
1060#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1061#[repr(transparent)]
1062struct PropertyIndices([u8; PropertyId::Unset as usize]);
1063
1064impl Default for PropertyIndices {
1065    fn default() -> Self {
1066        Self([PropertyId::Unset as u8; PropertyId::Unset as usize])
1067    }
1068}
1069
1070#[derive(Debug, Default)]
1071struct Properties {
1072    indices: PropertyIndices,
1073    values: Vec<PropertyValue>,
1074}
1075
1076fn value_at(values: &[PropertyValue], index: u8) -> &PropertyValue {
1077    if index == PropertyId::Unset as u8 {
1078        &PropertyValue::None
1079    } else {
1080        &values[index as usize]
1081    }
1082}
1083
1084impl PartialEq for Properties {
1085    fn eq(&self, other: &Self) -> bool {
1086        self.indices
1087            .0
1088            .iter()
1089            .zip(other.indices.0.iter())
1090            .all(|(&own, &other_index)| {
1091                value_at(&self.values, own) == value_at(&other.values, other_index)
1092            })
1093    }
1094}
1095
1096/// A single accessible object. A complete UI is represented as a tree of these.
1097///
1098/// For brevity, and to make more of the documentation usable in bindings
1099/// to other languages, documentation of getter methods is written as if
1100/// documenting fields in a struct, and such methods are referred to
1101/// as properties.
1102#[derive(Default, PartialEq)]
1103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1104#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1105#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
1106#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1107pub struct Node {
1108    role: Role,
1109    actions: u32,
1110    child_actions: u32,
1111    flags: u32,
1112    properties: Properties,
1113}
1114
1115impl PropertyIndices {
1116    fn get<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a PropertyValue {
1117        value_at(values, self.0[id as usize])
1118    }
1119}
1120
1121impl Properties {
1122    fn get_mut(&mut self, id: PropertyId, default: PropertyValue) -> &mut PropertyValue {
1123        let index = self.indices.0[id as usize] as usize;
1124        if index == PropertyId::Unset as usize {
1125            self.values.push(default);
1126            let index = self.values.len() - 1;
1127            self.indices.0[id as usize] = index as u8;
1128            &mut self.values[index]
1129        } else {
1130            let value = &mut self.values[index];
1131            if matches!(value, PropertyValue::None) {
1132                *value = default;
1133            }
1134            value
1135        }
1136    }
1137
1138    fn set(&mut self, id: PropertyId, value: PropertyValue) {
1139        let index = self.indices.0[id as usize];
1140        if index == PropertyId::Unset as u8 {
1141            self.values.push(value);
1142            self.indices.0[id as usize] = (self.values.len() - 1) as u8;
1143        } else {
1144            self.values[index as usize] = value;
1145        }
1146    }
1147
1148    fn clear(&mut self, id: PropertyId) {
1149        let index = self.indices.0[id as usize];
1150        if index != PropertyId::Unset as u8 {
1151            self.values[index as usize] = PropertyValue::None;
1152        }
1153    }
1154}
1155
1156impl Clone for Properties {
1157    fn clone(&self) -> Self {
1158        Self {
1159            indices: self.indices,
1160            values: self.values.clone(),
1161        }
1162    }
1163
1164    fn clone_from(&mut self, source: &Self) {
1165        self.indices = source.indices;
1166        source.values.clone_into(&mut self.values);
1167    }
1168}
1169
1170macro_rules! flag_methods {
1171    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1172        impl Node {
1173            $($(#[$doc])*
1174            #[inline]
1175            pub fn $getter(&self) -> bool {
1176                (self.flags & (Flag::$id).mask()) != 0
1177            }
1178            #[inline]
1179            pub fn $setter(&mut self) {
1180                self.flags |= (Flag::$id).mask();
1181            }
1182            #[inline]
1183            pub fn $clearer(&mut self) {
1184                self.flags &= !((Flag::$id).mask());
1185            })*
1186            fn debug_flag_properties(&self, fmt: &mut fmt::DebugStruct) {
1187                $(
1188                    if self.$getter() {
1189                        fmt.field(stringify!($getter), &true);
1190                    }
1191                )*
1192            }
1193        }
1194        $(#[cfg(test)]
1195        mod $getter {
1196            use super::{Node, Role};
1197
1198            #[test]
1199            fn getter_should_return_default_value() {
1200                let node = Node::new(Role::Unknown);
1201                assert!(!node.$getter());
1202            }
1203
1204            #[test]
1205            fn setter_should_update_the_property() {
1206                let mut node = Node::new(Role::Unknown);
1207                node.$setter();
1208                assert!(node.$getter());
1209            }
1210
1211            #[test]
1212            fn clearer_should_reset_the_property() {
1213                let mut node = Node::new(Role::Unknown);
1214                node.$setter();
1215                node.$clearer();
1216                assert!(!node.$getter());
1217            }
1218        })*
1219    }
1220}
1221
1222macro_rules! option_ref_type_getters {
1223    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1224        impl PropertyIndices {
1225            $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> Option<&'a $type> {
1226                match self.get(values, id) {
1227                    PropertyValue::$variant(value) => Some(value),
1228                    _ => None,
1229                }
1230            })*
1231        }
1232    }
1233}
1234
1235macro_rules! slice_type_getters {
1236    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1237        impl PropertyIndices {
1238            $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a [$type] {
1239                match self.get(values, id) {
1240                    PropertyValue::$variant(value) => value,
1241                    _ => &[],
1242                }
1243            })*
1244        }
1245    }
1246}
1247
1248macro_rules! copy_type_getters {
1249    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1250        impl PropertyIndices {
1251            $(fn $method(&self, values: &[PropertyValue], id: PropertyId) -> Option<$type> {
1252                match self.get(values, id) {
1253                    PropertyValue::$variant(value) => Some(*value),
1254                    _ => None,
1255                }
1256            })*
1257        }
1258    }
1259}
1260
1261macro_rules! owned_type_setters {
1262    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1263        impl Node {
1264            $(fn $method(&mut self, id: PropertyId, value: impl Into<$type>) {
1265                self.properties.set(id, PropertyValue::$variant(value.into()));
1266            })*
1267        }
1268    }
1269}
1270
1271macro_rules! copy_type_setters {
1272    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1273        impl Node {
1274            $(fn $method(&mut self, id: PropertyId, value: $type) {
1275                self.properties.set(id, PropertyValue::$variant(value));
1276            })*
1277        }
1278    }
1279}
1280
1281macro_rules! vec_type_methods {
1282    ($(($type:ty, $variant:ident, $getter:ident, $setter:ident, $pusher:ident)),+) => {
1283        $(slice_type_getters! {
1284            ($getter, $type, $variant)
1285        })*
1286        impl Node {
1287            $(fn $setter(&mut self, id: PropertyId, value: impl Into<Vec<$type>>) {
1288                self.properties.set(id, PropertyValue::$variant(value.into()));
1289            }
1290            fn $pusher(&mut self, id: PropertyId, item: $type) {
1291                if let PropertyValue::$variant(v) = self.properties.get_mut(id, PropertyValue::$variant(Vec::new())) {
1292                    v.push(item);
1293                }
1294            })*
1295        }
1296    }
1297}
1298
1299macro_rules! property_methods {
1300    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $type_getter:ident, $getter_result:ty, $setter:ident, $type_setter:ident, $setter_param:ty, $clearer:ident)),+) => {
1301        impl Node {
1302            $($(#[$doc])*
1303            #[inline]
1304            pub fn $getter(&self) -> $getter_result {
1305                self.properties.indices.$type_getter(&self.properties.values, PropertyId::$id)
1306            }
1307            #[inline]
1308            pub fn $setter(&mut self, value: $setter_param) {
1309                self.$type_setter(PropertyId::$id, value);
1310            }
1311            #[inline]
1312            pub fn $clearer(&mut self) {
1313                self.properties.clear(PropertyId::$id);
1314            })*
1315        }
1316    }
1317}
1318
1319macro_rules! vec_property_methods {
1320    ($($(#[$doc:meta])* ($id:ident, $item_type:ty, $getter:ident, $type_getter:ident, $setter:ident, $type_setter:ident, $pusher:ident, $type_pusher:ident, $clearer:ident)),+) => {
1321        $(property_methods! {
1322            $(#[$doc])*
1323            ($id, $getter, $type_getter, &[$item_type], $setter, $type_setter, impl Into<Vec<$item_type>>, $clearer)
1324        }
1325        impl Node {
1326            #[inline]
1327            pub fn $pusher(&mut self, item: $item_type) {
1328                self.$type_pusher(PropertyId::$id, item);
1329            }
1330        })*
1331    }
1332}
1333
1334macro_rules! vec_properties_debug_method {
1335    ($name:ident, [$($getter:ident,)*]) => {
1336        fn $name(&self, fmt: &mut fmt::DebugStruct) {
1337            $(
1338                let value = self.$getter();
1339                if !value.is_empty() {
1340                    fmt.field(stringify!($getter), &value);
1341                }
1342            )*
1343        }
1344    }
1345}
1346
1347macro_rules! node_id_vec_property_methods {
1348    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $pusher:ident, $clearer:ident)),+) => {
1349        $(vec_property_methods! {
1350            $(#[$doc])*
1351            ($id, NodeId, $getter, get_node_id_vec, $setter, set_node_id_vec, $pusher, push_to_node_id_vec, $clearer)
1352        })*
1353        impl Node {
1354            vec_properties_debug_method! { debug_node_id_vec_properties, [$($getter,)*] }
1355        }
1356        $(#[cfg(test)]
1357        mod $getter {
1358            use super::{Node, NodeId, Role};
1359
1360            #[test]
1361            fn getter_should_return_default_value() {
1362                let node = Node::new(Role::Unknown);
1363                assert!(node.$getter().is_empty());
1364            }
1365            #[test]
1366            fn setter_should_update_the_property() {
1367                let mut node = Node::new(Role::Unknown);
1368                node.$setter([]);
1369                assert!(node.$getter().is_empty());
1370                node.$setter([NodeId(0), NodeId(1)]);
1371                assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1372            }
1373            #[test]
1374            fn pusher_should_update_the_property() {
1375                let mut node = Node::new(Role::Unknown);
1376                node.$pusher(NodeId(0));
1377                assert_eq!(node.$getter(), &[NodeId(0)]);
1378                node.$pusher(NodeId(1));
1379                assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1380            }
1381            #[test]
1382            fn pusher_should_start_a_new_list_after_the_clearer() {
1383                let mut node = Node::new(Role::Unknown);
1384                node.$setter([NodeId(0)]);
1385                node.$clearer();
1386                node.$pusher(NodeId(1));
1387                assert_eq!(node.$getter(), &[NodeId(1)]);
1388            }
1389            #[test]
1390            fn clearer_should_reset_the_property() {
1391                let mut node = Node::new(Role::Unknown);
1392                node.$setter([NodeId(0)]);
1393                node.$clearer();
1394                assert!(node.$getter().is_empty());
1395            }
1396        })*
1397    }
1398}
1399
1400macro_rules! option_properties_debug_method {
1401    ($name:ident, [$($getter:ident,)*]) => {
1402        fn $name(&self, fmt: &mut fmt::DebugStruct) {
1403            $(
1404                if let Some(value) = self.$getter() {
1405                    fmt.field(stringify!($getter), &value);
1406                }
1407            )*
1408        }
1409    }
1410}
1411
1412macro_rules! node_id_property_methods {
1413    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1414        $(property_methods! {
1415            $(#[$doc])*
1416            ($id, $getter, get_node_id_property, Option<NodeId>, $setter, set_node_id_property, NodeId, $clearer)
1417        })*
1418        impl Node {
1419            option_properties_debug_method! { debug_node_id_properties, [$($getter,)*] }
1420        }
1421        $(#[cfg(test)]
1422        mod $getter {
1423            use super::{Node, NodeId, Role};
1424
1425            #[test]
1426            fn getter_should_return_default_value() {
1427                let node = Node::new(Role::Unknown);
1428                assert!(node.$getter().is_none());
1429            }
1430            #[test]
1431            fn setter_should_update_the_property() {
1432                let mut node = Node::new(Role::Unknown);
1433                node.$setter(NodeId(1));
1434                assert_eq!(node.$getter(), Some(NodeId(1)));
1435            }
1436            #[test]
1437            fn clearer_should_reset_the_property() {
1438                let mut node = Node::new(Role::Unknown);
1439                node.$setter(NodeId(1));
1440                node.$clearer();
1441                assert!(node.$getter().is_none());
1442            }
1443        })*
1444    }
1445}
1446
1447macro_rules! string_property_methods {
1448    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1449        $(property_methods! {
1450            $(#[$doc])*
1451            ($id, $getter, get_string_property, Option<&str>, $setter, set_string_property, impl Into<String>, $clearer)
1452        })*
1453        impl Node {
1454            option_properties_debug_method! { debug_string_properties, [$($getter,)*] }
1455        }
1456        $(#[cfg(test)]
1457        mod $getter {
1458            use super::{Node, Role};
1459
1460            #[test]
1461            fn getter_should_return_default_value() {
1462                let node = Node::new(Role::Unknown);
1463                assert!(node.$getter().is_none());
1464            }
1465            #[test]
1466            fn setter_should_update_the_property() {
1467                let mut node = Node::new(Role::Unknown);
1468                node.$setter("test");
1469                assert_eq!(node.$getter(), Some("test"));
1470            }
1471            #[test]
1472            fn clearer_should_reset_the_property() {
1473                let mut node = Node::new(Role::Unknown);
1474                node.$setter("test");
1475                node.$clearer();
1476                assert!(node.$getter().is_none());
1477            }
1478        })*
1479    }
1480}
1481
1482macro_rules! f64_property_methods {
1483    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1484        $(property_methods! {
1485            $(#[$doc])*
1486            ($id, $getter, get_f64_property, Option<f64>, $setter, set_f64_property, f64, $clearer)
1487        })*
1488        impl Node {
1489            option_properties_debug_method! { debug_f64_properties, [$($getter,)*] }
1490        }
1491        $(#[cfg(test)]
1492        mod $getter {
1493            use super::{Node, Role};
1494
1495            #[test]
1496            fn getter_should_return_default_value() {
1497                let node = Node::new(Role::Unknown);
1498                assert!(node.$getter().is_none());
1499            }
1500            #[test]
1501            fn setter_should_update_the_property() {
1502                let mut node = Node::new(Role::Unknown);
1503                node.$setter(1.0);
1504                assert_eq!(node.$getter(), Some(1.0));
1505            }
1506            #[test]
1507            fn clearer_should_reset_the_property() {
1508                let mut node = Node::new(Role::Unknown);
1509                node.$setter(1.0);
1510                node.$clearer();
1511                assert!(node.$getter().is_none());
1512            }
1513        })*
1514    }
1515}
1516
1517macro_rules! f32_property_methods {
1518    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1519        $(property_methods! {
1520            $(#[$doc])*
1521            ($id, $getter, get_f32_property, Option<f32>, $setter, set_f32_property, f32, $clearer)
1522        })*
1523        impl Node {
1524            option_properties_debug_method! { debug_f32_properties, [$($getter,)*] }
1525        }
1526        $(#[cfg(test)]
1527        mod $getter {
1528            use super::{Node, Role};
1529
1530            #[test]
1531            fn getter_should_return_default_value() {
1532                let node = Node::new(Role::Unknown);
1533                assert!(node.$getter().is_none());
1534            }
1535            #[test]
1536            fn setter_should_update_the_property() {
1537                let mut node = Node::new(Role::Unknown);
1538                node.$setter(1.0);
1539                assert_eq!(node.$getter(), Some(1.0));
1540            }
1541            #[test]
1542            fn clearer_should_reset_the_property() {
1543                let mut node = Node::new(Role::Unknown);
1544                node.$setter(1.0);
1545                node.$clearer();
1546                assert!(node.$getter().is_none());
1547            }
1548        })*
1549    }
1550}
1551
1552macro_rules! usize_property_methods {
1553    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1554        $(property_methods! {
1555            $(#[$doc])*
1556            ($id, $getter, get_usize_property, Option<usize>, $setter, set_usize_property, usize, $clearer)
1557        })*
1558        impl Node {
1559            option_properties_debug_method! { debug_usize_properties, [$($getter,)*] }
1560        }
1561        $(#[cfg(test)]
1562        mod $getter {
1563            use super::{Node, Role};
1564
1565            #[test]
1566            fn getter_should_return_default_value() {
1567                let node = Node::new(Role::Unknown);
1568                assert!(node.$getter().is_none());
1569            }
1570            #[test]
1571            fn setter_should_update_the_property() {
1572                let mut node = Node::new(Role::Unknown);
1573                node.$setter(1);
1574                assert_eq!(node.$getter(), Some(1));
1575            }
1576            #[test]
1577            fn clearer_should_reset_the_property() {
1578                let mut node = Node::new(Role::Unknown);
1579                node.$setter(1);
1580                node.$clearer();
1581                assert!(node.$getter().is_none());
1582            }
1583        })*
1584    }
1585}
1586
1587macro_rules! color_property_methods {
1588    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1589        $(property_methods! {
1590            $(#[$doc])*
1591            ($id, $getter, get_color_property, Option<Color>, $setter, set_color_property, Color, $clearer)
1592        })*
1593        impl Node {
1594            option_properties_debug_method! { debug_color_properties, [$($getter,)*] }
1595        }
1596        $(#[cfg(test)]
1597        mod $getter {
1598            use super::{Color, Node, Role};
1599
1600            #[test]
1601            fn getter_should_return_default_value() {
1602                let node = Node::new(Role::Unknown);
1603                assert!(node.$getter().is_none());
1604            }
1605            #[test]
1606            fn setter_should_update_the_property() {
1607                let mut node = Node::new(Role::Unknown);
1608                node.$setter(Color { red: 255, green: 255, blue: 255, alpha: 255 });
1609                assert_eq!(node.$getter(), Some(Color { red: 255, green: 255, blue: 255, alpha: 255 }));
1610            }
1611            #[test]
1612            fn clearer_should_reset_the_property() {
1613                let mut node = Node::new(Role::Unknown);
1614                node.$setter(Color { red: 255, green: 255, blue: 255, alpha: 255 });
1615                node.$clearer();
1616                assert!(node.$getter().is_none());
1617            }
1618        })*
1619    }
1620}
1621
1622macro_rules! text_decoration_property_methods {
1623    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1624        $(property_methods! {
1625            $(#[$doc])*
1626            ($id, $getter, get_text_decoration_property, Option<TextDecoration>, $setter, set_text_decoration_property, TextDecoration, $clearer)
1627        })*
1628        impl Node {
1629            option_properties_debug_method! { debug_text_decoration_properties, [$($getter,)*] }
1630        }
1631        $(#[cfg(test)]
1632        mod $getter {
1633            use super::{Color, Node, Role, TextDecoration, TextDecorationStyle};
1634
1635            const TEST_TEXT_DECORATION: TextDecoration = TextDecoration {
1636                style: TextDecorationStyle::Dotted,
1637                color: Color {
1638                    red: 0,
1639                    green: 0,
1640                    blue: 0,
1641                    alpha: 255,
1642                },
1643            };
1644
1645            #[test]
1646            fn getter_should_return_default_value() {
1647                let node = Node::new(Role::Unknown);
1648                assert!(node.$getter().is_none());
1649            }
1650            #[test]
1651            fn setter_should_update_the_property() {
1652                let mut node = Node::new(Role::Unknown);
1653                node.$setter(TEST_TEXT_DECORATION);
1654                assert_eq!(node.$getter(), Some(TEST_TEXT_DECORATION));
1655            }
1656            #[test]
1657            fn clearer_should_reset_the_property() {
1658                let mut node = Node::new(Role::Unknown);
1659                node.$setter(TEST_TEXT_DECORATION);
1660                node.$clearer();
1661                assert!(node.$getter().is_none());
1662            }
1663        })*
1664    }
1665}
1666
1667macro_rules! length_vec_property_methods {
1668    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1669        $(property_methods! {
1670            $(#[$doc])*
1671            ($id, $getter, get_length_vec_property, &[u8], $setter, set_length_vec_property, impl Into<Vec<u8>>, $clearer)
1672        })*
1673        impl Node {
1674            vec_properties_debug_method! { debug_length_vec_properties, [$($getter,)*] }
1675        }
1676        $(#[cfg(test)]
1677        mod $getter {
1678            use super::{Node, Role};
1679
1680            #[test]
1681            fn getter_should_return_default_value() {
1682                let node = Node::new(Role::Unknown);
1683                assert!(node.$getter().is_empty());
1684            }
1685            #[test]
1686            fn setter_should_update_the_property() {
1687                let mut node = Node::new(Role::Unknown);
1688                node.$setter([]);
1689                assert!(node.$getter().is_empty());
1690                node.$setter([1, 2]);
1691                assert_eq!(node.$getter(), &[1, 2]);
1692            }
1693            #[test]
1694            fn clearer_should_reset_the_property() {
1695                let mut node = Node::new(Role::Unknown);
1696                node.$setter([1, 2]);
1697                node.$clearer();
1698                assert!(node.$getter().is_empty());
1699            }
1700        })*
1701    }
1702}
1703
1704macro_rules! coord_vec_property_methods {
1705    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1706        $(property_methods! {
1707            $(#[$doc])*
1708            ($id, $getter, get_coord_vec_property, Option<&[f32]>, $setter, set_coord_vec_property, impl Into<Vec<f32>>, $clearer)
1709        })*
1710        impl Node {
1711            option_properties_debug_method! { debug_coord_vec_properties, [$($getter,)*] }
1712        }
1713        $(#[cfg(test)]
1714        mod $getter {
1715            use super::{Node, Role};
1716
1717            #[test]
1718            fn getter_should_return_default_value() {
1719                let node = Node::new(Role::Unknown);
1720                assert!(node.$getter().is_none());
1721            }
1722            #[test]
1723            fn setter_should_update_the_property() {
1724                let mut node = Node::new(Role::Unknown);
1725                node.$setter([]);
1726                let expected: Option<&[f32]> = Some(&[]);
1727                assert_eq!(node.$getter(), expected);
1728                node.$setter([1.0, 2.0]);
1729                let expected: Option<&[f32]> = Some(&[1.0, 2.0]);
1730                assert_eq!(node.$getter(), expected);
1731            }
1732            #[test]
1733            fn clearer_should_reset_the_property() {
1734                let mut node = Node::new(Role::Unknown);
1735                node.$setter([1.0, 2.0]);
1736                node.$clearer();
1737                assert!(node.$getter().is_none());
1738            }
1739        })*
1740    }
1741}
1742
1743macro_rules! bool_property_methods {
1744    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1745        $(property_methods! {
1746            $(#[$doc])*
1747            ($id, $getter, get_bool_property, Option<bool>, $setter, set_bool_property, bool, $clearer)
1748        })*
1749        impl Node {
1750            option_properties_debug_method! { debug_bool_properties, [$($getter,)*] }
1751        }
1752        $(#[cfg(test)]
1753        mod $getter {
1754            use super::{Node, Role};
1755
1756            #[test]
1757            fn getter_should_return_default_value() {
1758                let node = Node::new(Role::Unknown);
1759                assert!(node.$getter().is_none());
1760            }
1761            #[test]
1762            fn setter_should_update_the_property() {
1763                let mut node = Node::new(Role::Unknown);
1764                node.$setter(true);
1765                assert_eq!(node.$getter(), Some(true));
1766            }
1767            #[test]
1768            fn clearer_should_reset_the_property() {
1769                let mut node = Node::new(Role::Unknown);
1770                node.$setter(true);
1771                node.$clearer();
1772                assert!(node.$getter().is_none());
1773            }
1774        })*
1775    }
1776}
1777
1778macro_rules! unique_enum_property_methods {
1779    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident, $variant:ident)),+) => {
1780        impl Node {
1781            $($(#[$doc])*
1782            #[inline]
1783            pub fn $getter(&self) -> Option<$id> {
1784                match self.properties.indices.get(&self.properties.values, PropertyId::$id) {
1785                    PropertyValue::$id(value) => Some(*value),
1786                    _ => None,
1787                }
1788            }
1789            #[inline]
1790            pub fn $setter(&mut self, value: $id) {
1791                self.properties.set(PropertyId::$id, PropertyValue::$id(value));
1792            }
1793            #[inline]
1794            pub fn $clearer(&mut self) {
1795                self.properties.clear(PropertyId::$id);
1796            })*
1797            option_properties_debug_method! { debug_unique_enum_properties, [$($getter,)*] }
1798        }
1799        $(#[cfg(test)]
1800        mod $getter {
1801            use super::{Node, Role};
1802
1803            #[test]
1804            fn getter_should_return_default_value() {
1805                let node = Node::new(Role::Unknown);
1806                assert!(node.$getter().is_none());
1807            }
1808            #[test]
1809            fn setter_should_update_the_property() {
1810                let mut node = Node::new(Role::Unknown);
1811                let variant = super::$id::$variant;
1812                node.$setter(variant);
1813                assert_eq!(node.$getter(), Some(variant));
1814            }
1815            #[test]
1816            fn clearer_should_reset_the_property() {
1817                let mut node = Node::new(Role::Unknown);
1818                node.$setter(super::$id::$variant);
1819                node.$clearer();
1820                assert!(node.$getter().is_none());
1821            }
1822        })*
1823    }
1824}
1825
1826impl Node {
1827    #[inline]
1828    pub fn new(role: Role) -> Self {
1829        Self {
1830            role,
1831            ..Default::default()
1832        }
1833    }
1834}
1835
1836impl Clone for Node {
1837    fn clone(&self) -> Self {
1838        Self {
1839            role: self.role,
1840            actions: self.actions,
1841            child_actions: self.child_actions,
1842            flags: self.flags,
1843            properties: self.properties.clone(),
1844        }
1845    }
1846
1847    fn clone_from(&mut self, source: &Self) {
1848        self.role = source.role;
1849        self.actions = source.actions;
1850        self.child_actions = source.child_actions;
1851        self.flags = source.flags;
1852        self.properties.clone_from(&source.properties);
1853    }
1854}
1855
1856impl Node {
1857    #[inline]
1858    pub fn role(&self) -> Role {
1859        self.role
1860    }
1861    #[inline]
1862    pub fn set_role(&mut self, value: Role) {
1863        self.role = value;
1864    }
1865
1866    #[inline]
1867    pub fn supports_action(&self, action: Action) -> bool {
1868        (self.actions & action.mask()) != 0
1869    }
1870    #[inline]
1871    pub fn add_action(&mut self, action: Action) {
1872        self.actions |= action.mask();
1873    }
1874    #[inline]
1875    pub fn remove_action(&mut self, action: Action) {
1876        self.actions &= !(action.mask());
1877    }
1878    #[inline]
1879    pub fn clear_actions(&mut self) {
1880        self.actions = 0;
1881    }
1882
1883    /// Return whether the specified action is in the set supported on this node's
1884    /// direct children in the filtered tree.
1885    #[inline]
1886    pub fn child_supports_action(&self, action: Action) -> bool {
1887        (self.child_actions & action.mask()) != 0
1888    }
1889    /// Add the specified action to the set supported on this node's direct
1890    /// children in the filtered tree.
1891    #[inline]
1892    pub fn add_child_action(&mut self, action: Action) {
1893        self.child_actions |= action.mask();
1894    }
1895    /// Remove the specified action from the set supported on this node's direct
1896    /// children in the filtered tree.
1897    #[inline]
1898    pub fn remove_child_action(&mut self, action: Action) {
1899        self.child_actions &= !(action.mask());
1900    }
1901    /// Clear the set of actions supported on this node's direct children in the
1902    /// filtered tree.
1903    #[inline]
1904    pub fn clear_child_actions(&mut self) {
1905        self.child_actions = 0;
1906    }
1907}
1908
1909flag_methods! {
1910    /// Whether this node and its descendants are excluded from the tree
1911    /// presented to assistive technologies and from hit testing. ARIA
1912    /// equivalent: [`aria-hidden`].
1913    ///
1914    /// **Difference with ARIA:** `aria-hidden` controls exposure to assistive
1915    /// technologies, while this property also excludes the node from hit
1916    /// testing.
1917    ///
1918    /// [`aria-hidden`]: https://www.w3.org/TR/wai-aria-1.2/#aria-hidden
1919    (Hidden, is_hidden, set_hidden, clear_hidden),
1920    /// Whether users may select more than one selectable descendant. ARIA
1921    /// equivalent: [`aria-multiselectable`].
1922    ///
1923    /// [`aria-multiselectable`]: https://www.w3.org/TR/wai-aria-1.2/#aria-multiselectable
1924    (Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
1925    /// Whether user input or selection is required on this node. ARIA
1926    /// equivalent: [`aria-required`].
1927    ///
1928    /// [`aria-required`]: https://www.w3.org/TR/wai-aria-1.2/#aria-required
1929    (Required, is_required, set_required, clear_required),
1930    /// Whether this node represents a link that has been visited.
1931    (Visited, is_visited, set_visited, clear_visited),
1932    /// Whether this node is being modified and updates should be withheld until
1933    /// it is ready. ARIA equivalent: [`aria-busy`].
1934    ///
1935    /// [`aria-busy`]: https://www.w3.org/TR/wai-aria-1.2/#aria-busy
1936    (Busy, is_busy, set_busy, clear_busy),
1937    /// Whether updates to this live region should be presented as a whole. ARIA
1938    /// equivalent: [`aria-atomic`].
1939    ///
1940    /// [`aria-atomic`]: https://www.w3.org/TR/wai-aria-1.2/#aria-atomic
1941    (LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
1942    /// Whether this dialog is modal. ARIA equivalent: [`aria-modal`].
1943    ///
1944    /// [`aria-modal`]: https://www.w3.org/TR/wai-aria-1.2/#aria-modal
1945    (Modal, is_modal, set_modal, clear_modal),
1946    /// Whether touches pass through this node when a screen reader is in touch
1947    /// exploration mode, as they normally do on a virtual keyboard.
1948    (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1949    /// Whether this text widget permits focus and selection but not editing.
1950    /// ARIA equivalent: [`aria-readonly`].
1951    ///
1952    /// [`aria-readonly`]: https://www.w3.org/TR/wai-aria-1.2/#aria-readonly
1953    (ReadOnly, is_read_only, set_read_only, clear_read_only),
1954    /// Whether this control or group of controls is unavailable for interaction.
1955    /// ARIA equivalent: [`aria-disabled`].
1956    ///
1957    /// [`aria-disabled`]: https://www.w3.org/TR/wai-aria-1.2/#aria-disabled
1958    (Disabled, is_disabled, set_disabled, clear_disabled),
1959    /// Whether this node's text is italic.
1960    (Italic, is_italic, set_italic, clear_italic),
1961    /// Whether this node clips child content outside its bounds.
1962    (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1963    /// Whether this node causes a hard line break.
1964    (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1965    /// Whether this node causes a page break.
1966    (IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
1967    /// Whether this node's text is marked as a spelling error.
1968    (IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
1969    /// Whether this node's text is marked as a grammar error.
1970    (IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
1971    /// Whether this node represents text matching the current search.
1972    (IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
1973    /// Whether this node's text is a suggested replacement.
1974    (IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
1975}
1976
1977option_ref_type_getters! {
1978    (get_affine_property, Affine, Affine),
1979    (get_string_property, str, String),
1980    (get_coord_vec_property, [f32], CoordVec),
1981    (get_text_selection_property, TextSelection, TextSelection)
1982}
1983
1984slice_type_getters! {
1985    (get_length_vec_property, u8, LengthVec)
1986}
1987
1988copy_type_getters! {
1989    (get_rect_property, Rect, Rect),
1990    (get_node_id_property, NodeId, NodeId),
1991    (get_f64_property, f64, F64),
1992    (get_f32_property, f32, F32),
1993    (get_usize_property, usize, Usize),
1994    (get_color_property, Color, Color),
1995    (get_text_decoration_property, TextDecoration, TextDecoration),
1996    (get_bool_property, bool, Bool),
1997    (get_tree_id_property, TreeId, TreeId)
1998}
1999
2000owned_type_setters! {
2001    (set_affine_property, Box<Affine>, Affine),
2002    (set_string_property, String, String),
2003    (set_length_vec_property, Vec<u8>, LengthVec),
2004    (set_coord_vec_property, Vec<f32>, CoordVec),
2005    (set_text_selection_property, Box<TextSelection>, TextSelection)
2006}
2007
2008copy_type_setters! {
2009    (set_rect_property, Rect, Rect),
2010    (set_node_id_property, NodeId, NodeId),
2011    (set_f64_property, f64, F64),
2012    (set_f32_property, f32, F32),
2013    (set_usize_property, usize, Usize),
2014    (set_color_property, Color, Color),
2015    (set_text_decoration_property, TextDecoration, TextDecoration),
2016    (set_bool_property, bool, Bool),
2017    (set_tree_id_property, TreeId, TreeId)
2018}
2019
2020vec_type_methods! {
2021    (NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
2022    (CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
2023}
2024
2025node_id_vec_property_methods! {
2026    /// The ordered list of this node's direct children in the tree. The order
2027    /// should match the intended reading and navigation order.
2028    (Children, children, set_children, push_child, clear_children),
2029    /// The nodes whose contents or presence are controlled by this node. ARIA
2030    /// equivalent: [`aria-controls`].
2031    ///
2032    /// [`aria-controls`]: https://www.w3.org/TR/wai-aria-1.2/#aria-controls
2033    (Controls, controls, set_controls, push_controlled, clear_controls),
2034    /// The nodes that provide detailed, structured descriptions for this node
2035    /// and that users may navigate to. Use [`Node::described_by`] when a
2036    /// plain-text description is sufficient. ARIA equivalent: [`aria-details`].
2037    ///
2038    /// **Difference with ARIA:** `aria-details` references a single element,
2039    /// while this property may reference multiple nodes.
2040    ///
2041    /// [`aria-details`]: https://www.w3.org/TR/wai-aria-1.2/#aria-details
2042    (Details, details, set_details, push_detail, clear_details),
2043    /// The nodes whose contents provide a plain-text description for this node.
2044    /// Use [`Node::details`] for structured content that users may need to
2045    /// navigate. ARIA equivalent: [`aria-describedby`].
2046    ///
2047    /// [`aria-describedby`]: https://www.w3.org/TR/wai-aria-1.2/#aria-describedby
2048    (DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
2049    /// The nodes that follow this node in an alternate reading order. ARIA
2050    /// equivalent: [`aria-flowto`].
2051    ///
2052    /// [`aria-flowto`]: https://www.w3.org/TR/wai-aria-1.2/#aria-flowto
2053    (FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
2054    /// The nodes that label this node. ARIA equivalent: [`aria-labelledby`].
2055    ///
2056    /// [`aria-labelledby`]: https://www.w3.org/TR/wai-aria-1.2/#aria-labelledby
2057    (LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
2058    /// Nodes owned by this node that are not already its descendants in the
2059    /// tree. This property is unnecessary for direct children or other
2060    /// descendants. ARIA equivalent: [`aria-owns`].
2061    ///
2062    /// [`aria-owns`]: https://www.w3.org/TR/wai-aria-1.2/#aria-owns
2063    (Owns, owns, set_owns, push_owned, clear_owns),
2064    /// For a radio button, the list of all radio buttons in the same group,
2065    /// including this node.
2066    (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
2067}
2068
2069node_id_property_methods! {
2070    /// For a composite widget such as a listbox, tree, or grid, identifies
2071    /// the currently active descendant. Used when focus remains on the container
2072    /// while the active item changes. ARIA equivalent: [`aria-activedescendant`].
2073    ///
2074    /// [`aria-activedescendant`]: https://www.w3.org/TR/wai-aria-1.2/#aria-activedescendant
2075    (ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
2076    /// The node that provides an error message for this node. This should
2077    /// normally be used with [`Node::invalid`]. ARIA equivalent:
2078    /// [`aria-errormessage`].
2079    ///
2080    /// [`aria-errormessage`]: https://www.w3.org/TR/wai-aria-1.2/#aria-errormessage
2081    (ErrorMessage, error_message, set_error_message, clear_error_message),
2082    /// The target of this link when it points to another location in the same
2083    /// document.
2084    (InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
2085    /// The group to which this node belongs.
2086    (MemberOf, member_of, set_member_of, clear_member_of),
2087    /// The next text run on the same visual line. The target's
2088    /// [`Node::previous_on_line`] should point back to this node.
2089    (NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
2090    /// The previous text run on the same visual line. The target's
2091    /// [`Node::next_on_line`] should point back to this node.
2092    (PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
2093    /// The node for which this node is a popup.
2094    (PopupFor, popup_for, set_popup_for, clear_popup_for)
2095}
2096
2097string_property_methods! {
2098    /// The label of this node. If the label is specified via the
2099    /// [`Node::labelled_by`] relation, this doesn't need to be set. The text
2100    /// content of a node with the [`Role::Label`] role should be provided via
2101    /// [`Node::value`], not this property. ARIA equivalent: [`aria-label`].
2102    ///
2103    /// [`aria-label`]: https://www.w3.org/TR/wai-aria-1.2/#aria-label
2104    (Label, label, set_label, clear_label),
2105    /// Additional information that supplements this node's label. ARIA 1.3
2106    /// draft equivalent: [`aria-description`].
2107    ///
2108    /// [`aria-description`]: https://www.w3.org/TR/wai-aria-1.3/#aria-description
2109    (Description, description, set_description, clear_description),
2110    /// The text content or string value of this node. ARIA equivalent:
2111    /// [`aria-valuetext`] for range widgets.
2112    ///
2113    /// **Difference with ARIA:** Unlike `aria-valuetext`, this property is also
2114    /// used for text controls and the text content of nodes such as [`Role::Label`]
2115    /// and [`Role::TextRun`].
2116    ///
2117    /// [`aria-valuetext`]: https://www.w3.org/TR/wai-aria-1.2/#aria-valuetext
2118    (Value, value, set_value, clear_value),
2119    /// A single character, usually part of this node's name, that can be pressed,
2120    /// possibly along with a platform-specific modifier, to perform
2121    /// this node's default action. For menu items, the access key is only active
2122    /// while the menu is active, in contrast with [`keyboard_shortcut`];
2123    /// a single menu item may in fact have both properties.
2124    ///
2125    /// [`keyboard_shortcut`]: Node::keyboard_shortcut
2126    (AccessKey, access_key, set_access_key, clear_access_key),
2127    /// A way for application authors to identify this node for automated
2128    /// testing purposes. The value must be unique among this node's siblings.
2129    (AuthorId, author_id, set_author_id, clear_author_id),
2130    /// The platform control class name for this node.
2131    (ClassName, class_name, set_class_name, clear_class_name),
2132    /// The font family used for this node's text. Only set this when it differs
2133    /// from the parent.
2134    (FontFamily, font_family, set_font_family, clear_font_family),
2135    /// The ID of the HTML element represented by this node. On platforms
2136    /// where both are mapped to the same platform property, this takes
2137    /// precedence over [`author_id`].
2138    ///
2139    /// [`author_id`]: Node::author_id
2140    (HtmlId, html_id, set_html_id, clear_html_id),
2141    /// The name of the HTML element represented by this node.
2142    (HtmlTag, html_tag, set_html_tag, clear_html_tag),
2143    /// Inner HTML of an element. Only used for a top-level math element,
2144    /// to support third-party math accessibility products that parse MathML.
2145    (InnerHtml, inner_html, set_inner_html, clear_inner_html),
2146    /// A keystroke or sequence of keystrokes, complete with any required
2147    /// modifiers(s), that will perform this node's default action.
2148    /// The value of this property should be in a human-friendly format. ARIA
2149    /// equivalent: [`aria-keyshortcuts`].
2150    ///
2151    /// [`aria-keyshortcuts`]: https://www.w3.org/TR/wai-aria-1.2/#aria-keyshortcuts
2152    (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
2153    /// An [IETF language tag](https://www.rfc-editor.org/info/bcp47).
2154    /// Only set this when it differs from the parent.
2155    (Language, language, set_language, clear_language),
2156    /// A short hint that helps the user with data entry when the node has no
2157    /// [`Node::value`]. This property should not be used instead of
2158    /// [`Node::label`]. ARIA equivalent: [`aria-placeholder`].
2159    ///
2160    /// [`aria-placeholder`]: https://www.w3.org/TR/wai-aria-1.2/#aria-placeholder
2161    (Placeholder, placeholder, set_placeholder, clear_placeholder),
2162    /// An optional string that may override an assistive technology's
2163    /// description of the node's role. Only provide this for custom control types.
2164    /// The value should be human-friendly and localized. ARIA equivalent:
2165    /// [`aria-roledescription`].
2166    ///
2167    /// [`aria-roledescription`]: https://www.w3.org/TR/wai-aria-1.2/#aria-roledescription
2168    (RoleDescription, role_description, set_role_description, clear_role_description),
2169    /// An optional string that may override an assistive technology's
2170    /// description of the node's state, replacing default strings such as
2171    /// "checked" or "selected". Note that most platform accessibility APIs
2172    /// and assistive technologies do not support this feature.
2173    (StateDescription, state_description, set_state_description, clear_state_description),
2174    /// If a node's only label comes from a tooltip, it should be exposed through
2175    /// this property rather than [`label`].
2176    ///
2177    /// [`label`]: Node::label
2178    (Tooltip, tooltip, set_tooltip, clear_tooltip),
2179    /// The target URL of a link.
2180    (Url, url, set_url, clear_url),
2181    /// A human-readable alternative to the numeric row index. ARIA 1.3 draft
2182    /// equivalent: [`aria-rowindextext`].
2183    ///
2184    /// [`aria-rowindextext`]: https://www.w3.org/TR/wai-aria-1.3/#aria-rowindextext
2185    (RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
2186    /// A human-readable alternative to the numeric column index. ARIA 1.3 draft
2187    /// equivalent: [`aria-colindextext`].
2188    ///
2189    /// [`aria-colindextext`]: https://www.w3.org/TR/wai-aria-1.3/#aria-colindextext
2190    (ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text),
2191    /// An alternative to [`Node::label`] that assistive technologies may present
2192    /// on a Braille display instead of speaking it. The value is ordinary text,
2193    /// not Unicode Braille characters. ARIA 1.3 draft equivalent:
2194    /// [`aria-braillelabel`].
2195    ///
2196    /// [`aria-braillelabel`]: https://www.w3.org/TR/wai-aria-1.3/#aria-braillelabel
2197    (BrailleLabel, braille_label, set_braille_label, clear_braille_label),
2198    /// An alternative to [`Node::role_description`] that assistive technologies
2199    /// may present on a Braille display instead of speaking it. The value is
2200    /// ordinary text, not Unicode Braille characters. ARIA 1.3 draft equivalent:
2201    /// [`aria-brailleroledescription`].
2202    ///
2203    /// [`aria-brailleroledescription`]: https://www.w3.org/TR/wai-aria-1.3/#aria-brailleroledescription
2204    (BrailleRoleDescription, braille_role_description, set_braille_role_description, clear_braille_role_description)
2205}
2206
2207f64_property_methods! {
2208    /// The current horizontal scroll position of this scrollable container.
2209    (ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
2210    /// The minimum horizontal scroll position of this scrollable container.
2211    (ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
2212    /// The maximum horizontal scroll position of this scrollable container.
2213    (ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
2214    /// The current vertical scroll position of this scrollable container.
2215    (ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
2216    /// The minimum vertical scroll position of this scrollable container.
2217    (ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
2218    /// The maximum vertical scroll position of this scrollable container.
2219    (ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
2220    /// The current numeric value of this range widget. ARIA equivalent:
2221    /// [`aria-valuenow`].
2222    ///
2223    /// [`aria-valuenow`]: https://www.w3.org/TR/wai-aria-1.2/#aria-valuenow
2224    (NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
2225    /// The minimum allowed numeric value of this range widget. ARIA equivalent:
2226    /// [`aria-valuemin`].
2227    ///
2228    /// [`aria-valuemin`]: https://www.w3.org/TR/wai-aria-1.2/#aria-valuemin
2229    (MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
2230    /// The maximum allowed numeric value of this range widget. ARIA equivalent:
2231    /// [`aria-valuemax`].
2232    ///
2233    /// [`aria-valuemax`]: https://www.w3.org/TR/wai-aria-1.2/#aria-valuemax
2234    (MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
2235    /// The amount by which the numeric value changes when incremented or
2236    /// decremented by one step.
2237    (NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
2238    /// The amount by which the numeric value changes for a large increment or
2239    /// decrement.
2240    (NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump)
2241}
2242
2243f32_property_methods! {
2244    /// Font size is in pixels.
2245    (FontSize, font_size, set_font_size, clear_font_size),
2246    /// Font weight can take on any arbitrary numeric value. Increments of 100 in
2247    /// range `[0, 900]` represent keywords such as light, normal, bold, etc.
2248    (FontWeight, font_weight, set_font_weight, clear_font_weight)
2249}
2250
2251usize_property_methods! {
2252    /// The total number of rows in a table, grid, or tree grid. ARIA equivalent:
2253    /// [`aria-rowcount`].
2254    ///
2255    /// [`aria-rowcount`]: https://www.w3.org/TR/wai-aria-1.2/#aria-rowcount
2256    (RowCount, row_count, set_row_count, clear_row_count),
2257    /// The total number of columns in a table, grid, or tree grid. ARIA
2258    /// equivalent: [`aria-colcount`].
2259    ///
2260    /// [`aria-colcount`]: https://www.w3.org/TR/wai-aria-1.2/#aria-colcount
2261    (ColumnCount, column_count, set_column_count, clear_column_count),
2262    /// The row index of this node within a table, grid, or tree grid. ARIA
2263    /// equivalent: [`aria-rowindex`].
2264    ///
2265    /// **Difference with ARIA:** `aria-rowindex` is one-based, while this
2266    /// property is zero-based.
2267    ///
2268    /// [`aria-rowindex`]: https://www.w3.org/TR/wai-aria-1.2/#aria-rowindex
2269    (RowIndex, row_index, set_row_index, clear_row_index),
2270    /// The column index of this node within a table, grid, or tree grid. ARIA
2271    /// equivalent: [`aria-colindex`].
2272    ///
2273    /// **Difference with ARIA:** `aria-colindex` is one-based, while this
2274    /// property is zero-based.
2275    ///
2276    /// [`aria-colindex`]: https://www.w3.org/TR/wai-aria-1.2/#aria-colindex
2277    (ColumnIndex, column_index, set_column_index, clear_column_index),
2278    /// The number of rows spanned by a cell or grid cell. ARIA equivalent:
2279    /// [`aria-rowspan`].
2280    ///
2281    /// [`aria-rowspan`]: https://www.w3.org/TR/wai-aria-1.2/#aria-rowspan
2282    (RowSpan, row_span, set_row_span, clear_row_span),
2283    /// The number of columns spanned by a cell or grid cell. ARIA equivalent:
2284    /// [`aria-colspan`].
2285    ///
2286    /// [`aria-colspan`]: https://www.w3.org/TR/wai-aria-1.2/#aria-colspan
2287    (ColumnSpan, column_span, set_column_span, clear_column_span),
2288    /// The hierarchical level of this node within a structure. ARIA equivalent:
2289    /// [`aria-level`].
2290    ///
2291    /// **Difference with ARIA:** `aria-level` is one-based, while this property
2292    /// is zero-based.
2293    ///
2294    /// [`aria-level`]: https://www.w3.org/TR/wai-aria-1.2/#aria-level
2295    (Level, level, set_level, clear_level),
2296    /// For containers like [`Role::ListBox`], specifies the total number of items.
2297    /// ARIA equivalent: [`aria-setsize`].
2298    ///
2299    /// **Difference with ARIA:** `aria-setsize` is set on each item, while this
2300    /// property is set on the container.
2301    ///
2302    /// [`aria-setsize`]: https://www.w3.org/TR/wai-aria-1.2/#aria-setsize
2303    (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
2304    /// For items like [`Role::ListBoxOption`], specifies their index in the item list.
2305    /// This must be less than the value of [`size_of_set`] as set on the container.
2306    /// ARIA equivalent: [`aria-posinset`].
2307    ///
2308    /// **Difference with ARIA:** `aria-posinset` is one-based, while this
2309    /// property is zero-based.
2310    ///
2311    /// [`size_of_set`]: Node::size_of_set
2312    /// [`aria-posinset`]: https://www.w3.org/TR/wai-aria-1.2/#aria-posinset
2313    (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
2314}
2315
2316color_property_methods! {
2317    /// For [`Role::ColorWell`], specifies the selected color.
2318    (ColorValue, color_value, set_color_value, clear_color_value),
2319    /// Background color.
2320    (BackgroundColor, background_color, set_background_color, clear_background_color),
2321    /// Foreground color.
2322    (ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
2323}
2324
2325text_decoration_property_methods! {
2326    /// The style and color of the overline applied to this node's text.
2327    (Overline, overline, set_overline, clear_overline),
2328    /// The style and color of the strikethrough applied to this node's text.
2329    (Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
2330    /// The style and color of the underline applied to this node's text.
2331    (Underline, underline, set_underline, clear_underline)
2332}
2333
2334length_vec_property_methods! {
2335    /// For text runs, the length (non-inclusive) of each character
2336    /// in UTF-8 code units (bytes). The sum of these lengths must equal
2337    /// the length of [`value`], also in bytes.
2338    ///
2339    /// A character is defined as the smallest unit of text that
2340    /// can be selected. This isn't necessarily a single Unicode
2341    /// scalar value (code point). This is why AccessKit can't compute
2342    /// the lengths of the characters from the text itself; this information
2343    /// must be provided by the text editing implementation.
2344    ///
2345    /// If this node is the last text run in a line that ends with a hard
2346    /// line break, that line break should be included at the end of this
2347    /// node's value as either a CRLF or LF; in both cases, the line break
2348    /// should be counted as a single character for the sake of this slice.
2349    /// When the caret is at the end of such a line, the focus of the text
2350    /// selection should be on the line break, not after it.
2351    ///
2352    /// [`value`]: Node::value
2353    (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
2354
2355    /// For text runs, the start index of each word in characters, as defined
2356    /// in [`character_lengths`]. This list must be sorted.
2357    ///
2358    /// If this text run doesn't contain the start of any words, but only
2359    /// the middle or end of a word, this list must be empty.
2360    ///
2361    /// If this text run is the first in the document or the first in a paragraph
2362    /// (that is, the previous run ends with a newline character), then the first
2363    /// character of the run is implicitly the start of a word. In this case,
2364    /// beginning this list with `0` is permitted but not necessary.
2365    ///
2366    /// The end of each word is the beginning of the next word; there are no
2367    /// characters that are not considered part of a word. Trailing whitespace
2368    /// is typically considered part of the word that precedes it, while
2369    /// a line's leading whitespace is considered its own word. Whether
2370    /// punctuation is considered a separate word or part of the preceding
2371    /// word depends on the particular text editing implementation.
2372    /// Some editors may have their own definition of a word; for example,
2373    /// in an IDE, words may correspond to programming language tokens.
2374    ///
2375    /// Not all assistive technologies require information about word
2376    /// boundaries, and not all platform accessibility APIs even expose
2377    /// this information, but for assistive technologies that do use
2378    /// this information, users will get unpredictable results if the word
2379    /// boundaries exposed by the accessibility tree don't match
2380    /// the editor's behavior. This is why AccessKit does not determine
2381    /// word boundaries itself.
2382    ///
2383    /// [`character_lengths`]: Node::character_lengths
2384    (WordStarts, word_starts, set_word_starts, clear_word_starts)
2385}
2386
2387coord_vec_property_methods! {
2388    /// For text runs, this is the position of each character within
2389    /// the node's bounding box, in the direction given by
2390    /// [`text_direction`], in the coordinate space of this node.
2391    ///
2392    /// When present, the length of this slice should be the same as the length
2393    /// of [`character_lengths`], including for lines that end
2394    /// with a hard line break. The position of such a line break should
2395    /// be the position where an end-of-paragraph marker would be rendered.
2396    ///
2397    /// This property is optional. Without it, AccessKit can't support some
2398    /// use cases, such as screen magnifiers that track the caret position
2399    /// or screen readers that display a highlight cursor. However,
2400    /// most text functionality still works without this information.
2401    ///
2402    /// [`text_direction`]: Node::text_direction
2403    /// [`character_lengths`]: Node::character_lengths
2404    (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
2405
2406    /// For text runs, this is the advance width of each character,
2407    /// in the direction given by [`text_direction`], in the coordinate
2408    /// space of this node.
2409    ///
2410    /// When present, the length of this slice should be the same as the length
2411    /// of [`character_lengths`], including for lines that end
2412    /// with a hard line break. The width of such a line break should
2413    /// be non-zero if selecting the line break by itself results in
2414    /// a visible highlight (as in Microsoft Word), or zero if not
2415    /// (as in Windows Notepad).
2416    ///
2417    /// This property is optional. Without it, AccessKit can't support some
2418    /// use cases, such as screen magnifiers that track the caret position
2419    /// or screen readers that display a highlight cursor. However,
2420    /// most text functionality still works without this information.
2421    ///
2422    /// [`text_direction`]: Node::text_direction
2423    /// [`character_lengths`]: Node::character_lengths
2424    (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
2425}
2426
2427bool_property_methods! {
2428    /// Whether this node is expanded, collapsed, or neither. ARIA equivalent:
2429    /// [`aria-expanded`].
2430    ///
2431    /// Setting this to `false` means the node is collapsed; omitting it means this state
2432    /// isn't applicable.
2433    ///
2434    /// [`aria-expanded`]: https://www.w3.org/TR/wai-aria-1.2/#aria-expanded
2435    (Expanded, is_expanded, set_expanded, clear_expanded),
2436
2437    /// Indicates whether this node is selected or unselected. ARIA equivalent:
2438    /// [`aria-selected`].
2439    ///
2440    /// The absence of this flag (as opposed to a `false` setting)
2441    /// means that the concept of "selected" doesn't apply.
2442    /// When deciding whether to set the flag to false or omit it,
2443    /// consider whether it would be appropriate for a screen reader
2444    /// to announce "not selected". The ambiguity of this flag
2445    /// in platform accessibility APIs has made extraneous
2446    /// "not selected" announcements a common annoyance.
2447    ///
2448    /// [`aria-selected`]: https://www.w3.org/TR/wai-aria-1.2/#aria-selected
2449    (Selected, is_selected, set_selected, clear_selected)
2450}
2451
2452unique_enum_property_methods! {
2453    /// Whether this node's input is invalid, and whether the error is related
2454    /// to spelling or grammar. ARIA equivalent: [`aria-invalid`].
2455    ///
2456    /// [`aria-invalid`]: https://www.w3.org/TR/wai-aria-1.2/#aria-invalid
2457    (Invalid, invalid, set_invalid, clear_invalid, Grammar),
2458    /// The checked or pressed state of a toggle control. ARIA equivalents:
2459    /// [`aria-checked`] and [`aria-pressed`].
2460    ///
2461    /// **Difference with ARIA:** ARIA uses separate states for checked and
2462    /// pressed controls, while this property is interpreted based on the
2463    /// node's role.
2464    ///
2465    /// [`aria-checked`]: https://www.w3.org/TR/wai-aria-1.2/#aria-checked
2466    /// [`aria-pressed`]: https://www.w3.org/TR/wai-aria-1.2/#aria-pressed
2467    (Toggled, toggled, set_toggled, clear_toggled, True),
2468    /// The priority with which updates to this live region should be announced.
2469    /// ARIA equivalent: [`aria-live`].
2470    ///
2471    /// [`aria-live`]: https://www.w3.org/TR/wai-aria-1.2/#aria-live
2472    (Live, live, set_live, clear_live, Polite),
2473    /// The direction in which this node's text is laid out.
2474    (TextDirection, text_direction, set_text_direction, clear_text_direction, RightToLeft),
2475    /// Whether this node is oriented horizontally or vertically. ARIA
2476    /// equivalent: [`aria-orientation`].
2477    ///
2478    /// [`aria-orientation`]: https://www.w3.org/TR/wai-aria-1.2/#aria-orientation
2479    (Orientation, orientation, set_orientation, clear_orientation, Vertical),
2480    /// The direction in which items are sorted by this row or column header.
2481    /// ARIA equivalent: [`aria-sort`].
2482    ///
2483    /// [`aria-sort`]: https://www.w3.org/TR/wai-aria-1.2/#aria-sort
2484    (SortDirection, sort_direction, set_sort_direction, clear_sort_direction, Descending),
2485    /// Whether and how this node represents the current item within a set. ARIA
2486    /// equivalent: [`aria-current`].
2487    ///
2488    /// [`aria-current`]: https://www.w3.org/TR/wai-aria-1.2/#aria-current
2489    (AriaCurrent, aria_current, set_aria_current, clear_aria_current, True),
2490    /// How predictions are presented while the user enters text. ARIA
2491    /// equivalent: [`aria-autocomplete`].
2492    ///
2493    /// [`aria-autocomplete`]: https://www.w3.org/TR/wai-aria-1.2/#aria-autocomplete
2494    (AutoComplete, auto_complete, set_auto_complete, clear_auto_complete, List),
2495    /// The type of interactive popup that can be triggered by this node. ARIA
2496    /// equivalent: [`aria-haspopup`].
2497    ///
2498    /// [`aria-haspopup`]: https://www.w3.org/TR/wai-aria-1.2/#aria-haspopup
2499    (HasPopup, has_popup, set_has_popup, clear_has_popup, Menu),
2500    /// The list marker style. Only available on list items.
2501    (ListStyle, list_style, set_list_style, clear_list_style, Disc),
2502    /// How this node's text is aligned horizontally.
2503    (TextAlign, text_align, set_text_align, clear_text_align, Right),
2504    /// Whether this node's text is rendered as subscript or superscript.
2505    (VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset, Superscript)
2506}
2507
2508property_methods! {
2509    /// An affine transform to apply to any coordinates within this node
2510    /// and its descendants, including the [`bounds`] property of this node.
2511    /// The combined transforms of this node and its ancestors define
2512    /// the coordinate space of this node. /// This should be `None` if
2513    /// it would be set to the identity transform, which should be the case
2514    /// for most nodes.
2515    ///
2516    /// AccessKit expects the final transformed coordinates to be relative
2517    /// to the origin of the tree's container (e.g. window), in physical
2518    /// pixels, with the y coordinate being top-down.
2519    ///
2520    /// [`bounds`]: Node::bounds
2521    (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
2522
2523    /// The bounding box of this node, in the node's coordinate space.
2524    /// This property does not affect the coordinate space of either this node
2525    /// or its descendants; only the [`transform`] property affects that.
2526    /// This, along with the recommendation that most nodes should have
2527    /// a [`transform`] of `None`, implies that the `bounds` property
2528    /// of most nodes should be in the coordinate space of the nearest ancestor
2529    /// with a non-`None` [`transform`], or if there is no such ancestor,
2530    /// the tree's container (e.g. window).
2531    ///
2532    /// [`transform`]: Node::transform
2533    (Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
2534
2535    /// The current text selection or caret position within this node's text.
2536    (TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection),
2537
2538    /// The tree that this node grafts. When set, this node acts as a graft
2539    /// point, and its child is the root of the specified subtree.
2540    ///
2541    /// A graft node must be created before its subtree is pushed.
2542    ///
2543    /// Removing a graft node or clearing this property removes its subtree,
2544    /// unless a new graft node is provided in the same update.
2545    (TreeId, tree_id, get_tree_id_property, Option<TreeId>, set_tree_id, set_tree_id_property, TreeId, clear_tree_id)
2546}
2547
2548impl Node {
2549    option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection, tree_id,] }
2550}
2551
2552#[cfg(test)]
2553mod transform {
2554    use super::{Affine, Node, Role};
2555
2556    #[test]
2557    fn getter_should_return_default_value() {
2558        let node = Node::new(Role::Unknown);
2559        assert!(node.transform().is_none());
2560    }
2561    #[test]
2562    fn setter_should_update_the_property() {
2563        let mut node = Node::new(Role::Unknown);
2564        node.set_transform(Affine::IDENTITY);
2565        assert_eq!(node.transform(), Some(&Affine::IDENTITY));
2566    }
2567    #[test]
2568    fn clearer_should_reset_the_property() {
2569        let mut node = Node::new(Role::Unknown);
2570        node.set_transform(Affine::IDENTITY);
2571        node.clear_transform();
2572        assert!(node.transform().is_none());
2573    }
2574}
2575
2576#[cfg(test)]
2577mod bounds {
2578    use super::{Node, Rect, Role};
2579
2580    #[test]
2581    fn getter_should_return_default_value() {
2582        let node = Node::new(Role::Unknown);
2583        assert!(node.bounds().is_none());
2584    }
2585    #[test]
2586    fn setter_should_update_the_property() {
2587        let mut node = Node::new(Role::Unknown);
2588        let value = Rect {
2589            x0: 0.0,
2590            y0: 1.0,
2591            x1: 2.0,
2592            y1: 3.0,
2593        };
2594        node.set_bounds(value);
2595        assert_eq!(node.bounds(), Some(value));
2596    }
2597    #[test]
2598    fn clearer_should_reset_the_property() {
2599        let mut node = Node::new(Role::Unknown);
2600        node.set_bounds(Rect {
2601            x0: 0.0,
2602            y0: 1.0,
2603            x1: 2.0,
2604            y1: 3.0,
2605        });
2606        node.clear_bounds();
2607        assert!(node.bounds().is_none());
2608    }
2609}
2610
2611#[cfg(test)]
2612mod text_selection {
2613    use super::{Node, NodeId, Role, TextPosition, TextSelection};
2614
2615    #[test]
2616    fn getter_should_return_default_value() {
2617        let node = Node::new(Role::Unknown);
2618        assert!(node.text_selection().is_none());
2619    }
2620    #[test]
2621    fn setter_should_update_the_property() {
2622        let mut node = Node::new(Role::Unknown);
2623        let value = TextSelection {
2624            anchor: TextPosition {
2625                node: NodeId(0),
2626                character_index: 0,
2627            },
2628            focus: TextPosition {
2629                node: NodeId(0),
2630                character_index: 2,
2631            },
2632        };
2633        node.set_text_selection(value);
2634        assert_eq!(node.text_selection(), Some(&value));
2635    }
2636    #[test]
2637    fn clearer_should_reset_the_property() {
2638        let mut node = Node::new(Role::Unknown);
2639        node.set_text_selection(TextSelection {
2640            anchor: TextPosition {
2641                node: NodeId(0),
2642                character_index: 0,
2643            },
2644            focus: TextPosition {
2645                node: NodeId(0),
2646                character_index: 2,
2647            },
2648        });
2649        node.clear_text_selection();
2650        assert!(node.text_selection().is_none());
2651    }
2652}
2653
2654#[cfg(test)]
2655mod tree_id {
2656    use super::{Node, Role, TreeId, Uuid};
2657
2658    #[test]
2659    fn getter_should_return_default_value() {
2660        let node = Node::new(Role::GenericContainer);
2661        assert!(node.tree_id().is_none());
2662    }
2663    #[test]
2664    fn setter_should_update_the_property() {
2665        let mut node = Node::new(Role::GenericContainer);
2666        let value = TreeId(Uuid::nil());
2667        node.set_tree_id(value);
2668        assert_eq!(node.tree_id(), Some(value));
2669    }
2670    #[test]
2671    fn clearer_should_reset_the_property() {
2672        let mut node = Node::new(Role::GenericContainer);
2673        node.set_tree_id(TreeId(Uuid::nil()));
2674        node.clear_tree_id();
2675        assert!(node.tree_id().is_none());
2676    }
2677}
2678
2679vec_property_methods! {
2680    /// The application-defined actions supported by this node.
2681    (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)
2682}
2683
2684#[cfg(test)]
2685mod custom_actions {
2686    use super::{CustomAction, Node, Role};
2687    use core::slice;
2688
2689    #[test]
2690    fn getter_should_return_default_value() {
2691        let node = Node::new(Role::Unknown);
2692        assert!(node.custom_actions().is_empty());
2693    }
2694    #[test]
2695    fn setter_should_update_the_property() {
2696        let mut node = Node::new(Role::Unknown);
2697        let value = alloc::vec![
2698            CustomAction {
2699                id: 0,
2700                description: "first test action".into(),
2701            },
2702            CustomAction {
2703                id: 1,
2704                description: "second test action".into(),
2705            },
2706        ];
2707        node.set_custom_actions(value.clone());
2708        assert_eq!(node.custom_actions(), value);
2709    }
2710    #[test]
2711    fn pusher_should_update_the_property() {
2712        let mut node = Node::new(Role::Unknown);
2713        let first_action = CustomAction {
2714            id: 0,
2715            description: "first test action".into(),
2716        };
2717        let second_action = CustomAction {
2718            id: 1,
2719            description: "second test action".into(),
2720        };
2721        node.push_custom_action(first_action.clone());
2722        assert_eq!(node.custom_actions(), slice::from_ref(&first_action));
2723        node.push_custom_action(second_action.clone());
2724        assert_eq!(node.custom_actions(), &[first_action, second_action]);
2725    }
2726    #[test]
2727    fn pusher_should_start_a_new_list_after_the_clearer() {
2728        let mut node = Node::new(Role::Unknown);
2729        node.set_custom_actions([CustomAction {
2730            id: 0,
2731            description: "first test action".into(),
2732        }]);
2733        node.clear_custom_actions();
2734        let second_action = CustomAction {
2735            id: 1,
2736            description: "second test action".into(),
2737        };
2738        node.push_custom_action(second_action.clone());
2739        assert_eq!(node.custom_actions(), slice::from_ref(&second_action));
2740    }
2741    #[test]
2742    fn clearer_should_reset_the_property() {
2743        let mut node = Node::new(Role::Unknown);
2744        node.set_custom_actions([CustomAction {
2745            id: 0,
2746            description: "test action".into(),
2747        }]);
2748        node.clear_custom_actions();
2749        assert!(node.custom_actions().is_empty());
2750    }
2751}
2752
2753impl fmt::Debug for Node {
2754    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2755        let mut fmt = f.debug_struct("Node");
2756
2757        fmt.field("role", &self.role());
2758
2759        let supported_actions = action_mask_to_action_vec(self.actions);
2760        if !supported_actions.is_empty() {
2761            fmt.field("actions", &supported_actions);
2762        }
2763
2764        let child_supported_actions = action_mask_to_action_vec(self.child_actions);
2765        if !child_supported_actions.is_empty() {
2766            fmt.field("child_actions", &child_supported_actions);
2767        }
2768
2769        self.debug_flag_properties(&mut fmt);
2770        self.debug_node_id_vec_properties(&mut fmt);
2771        self.debug_node_id_properties(&mut fmt);
2772        self.debug_string_properties(&mut fmt);
2773        self.debug_f64_properties(&mut fmt);
2774        self.debug_f32_properties(&mut fmt);
2775        self.debug_usize_properties(&mut fmt);
2776        self.debug_color_properties(&mut fmt);
2777        self.debug_text_decoration_properties(&mut fmt);
2778        self.debug_length_vec_properties(&mut fmt);
2779        self.debug_coord_vec_properties(&mut fmt);
2780        self.debug_bool_properties(&mut fmt);
2781        self.debug_unique_enum_properties(&mut fmt);
2782        self.debug_option_properties(&mut fmt);
2783
2784        let custom_actions = self.custom_actions();
2785        if !custom_actions.is_empty() {
2786            fmt.field("custom_actions", &custom_actions);
2787        }
2788
2789        fmt.finish()
2790    }
2791}
2792
2793#[cfg(feature = "serde")]
2794macro_rules! serialize_property {
2795    ($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
2796        match &$self.values[$index as usize] {
2797            PropertyValue::None => (),
2798            $(PropertyValue::$variant(value) => {
2799                $map.serialize_entry(&$id, &value)?;
2800            })*
2801        }
2802    }
2803}
2804
2805#[cfg(feature = "serde")]
2806macro_rules! deserialize_property {
2807    ($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
2808        match $key {
2809            $($(PropertyId::$id => {
2810                let value = $map.next_value()?;
2811                $props.set(PropertyId::$id, PropertyValue::$type(value));
2812            })*)*
2813            PropertyId::Unset => {
2814                let _ = $map.next_value::<IgnoredAny>()?;
2815            }
2816        }
2817    }
2818}
2819
2820#[cfg(feature = "serde")]
2821impl Serialize for Properties {
2822    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2823    where
2824        S: Serializer,
2825    {
2826        let mut len = 0;
2827        for value in &*self.values {
2828            if !matches!(*value, PropertyValue::None) {
2829                len += 1;
2830            }
2831        }
2832        let mut map = serializer.serialize_map(Some(len))?;
2833        for (id, index) in self.indices.0.iter().copied().enumerate() {
2834            if index == PropertyId::Unset as u8 {
2835                continue;
2836            }
2837            let id = PropertyId::n(id as _).unwrap();
2838            serialize_property!(self, map, index, id, {
2839                NodeIdVec,
2840                NodeId,
2841                String,
2842                F64,
2843                F32,
2844                Usize,
2845                Color,
2846                TextDecoration,
2847                LengthVec,
2848                CoordVec,
2849                Bool,
2850                Invalid,
2851                Toggled,
2852                Live,
2853                TextDirection,
2854                Orientation,
2855                SortDirection,
2856                AriaCurrent,
2857                AutoComplete,
2858                HasPopup,
2859                ListStyle,
2860                TextAlign,
2861                VerticalOffset,
2862                Affine,
2863                Rect,
2864                TextSelection,
2865                CustomActionVec,
2866                TreeId
2867            });
2868        }
2869        map.end()
2870    }
2871}
2872
2873#[cfg(feature = "serde")]
2874struct PropertiesVisitor;
2875
2876#[cfg(feature = "serde")]
2877impl<'de> Visitor<'de> for PropertiesVisitor {
2878    type Value = Properties;
2879
2880    #[inline]
2881    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2882        formatter.write_str("property map")
2883    }
2884
2885    fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
2886    where
2887        V: MapAccess<'de>,
2888    {
2889        let mut props = Properties::default();
2890        while let Some(id) = map.next_key()? {
2891            deserialize_property!(props, map, id, {
2892                NodeIdVec {
2893                    Children,
2894                    Controls,
2895                    Details,
2896                    DescribedBy,
2897                    FlowTo,
2898                    LabelledBy,
2899                    Owns,
2900                    RadioGroup
2901                },
2902                NodeId {
2903                    ActiveDescendant,
2904                    ErrorMessage,
2905                    InPageLinkTarget,
2906                    MemberOf,
2907                    NextOnLine,
2908                    PreviousOnLine,
2909                    PopupFor
2910                },
2911                String {
2912                    Label,
2913                    Description,
2914                    Value,
2915                    AccessKey,
2916                    AuthorId,
2917                    ClassName,
2918                    FontFamily,
2919                    HtmlId,
2920                    HtmlTag,
2921                    InnerHtml,
2922                    KeyboardShortcut,
2923                    Language,
2924                    Placeholder,
2925                    RoleDescription,
2926                    StateDescription,
2927                    Tooltip,
2928                    Url,
2929                    RowIndexText,
2930                    ColumnIndexText,
2931                    BrailleLabel,
2932                    BrailleRoleDescription
2933                },
2934                F64 {
2935                    ScrollX,
2936                    ScrollXMin,
2937                    ScrollXMax,
2938                    ScrollY,
2939                    ScrollYMin,
2940                    ScrollYMax,
2941                    NumericValue,
2942                    MinNumericValue,
2943                    MaxNumericValue,
2944                    NumericValueStep,
2945                    NumericValueJump
2946                },
2947                F32 {
2948                    FontSize,
2949                    FontWeight
2950                },
2951                Usize {
2952                    RowCount,
2953                    ColumnCount,
2954                    RowIndex,
2955                    ColumnIndex,
2956                    RowSpan,
2957                    ColumnSpan,
2958                    Level,
2959                    SizeOfSet,
2960                    PositionInSet
2961                },
2962                Color {
2963                    ColorValue,
2964                    BackgroundColor,
2965                    ForegroundColor
2966                },
2967                TextDecoration {
2968                    Overline,
2969                    Strikethrough,
2970                    Underline
2971                },
2972                LengthVec {
2973                    CharacterLengths,
2974                    WordStarts
2975                },
2976                CoordVec {
2977                    CharacterPositions,
2978                    CharacterWidths
2979                },
2980                Bool {
2981                    Expanded,
2982                    Selected
2983                },
2984                Invalid { Invalid },
2985                Toggled { Toggled },
2986                Live { Live },
2987                TextDirection { TextDirection },
2988                Orientation { Orientation },
2989                SortDirection { SortDirection },
2990                AriaCurrent { AriaCurrent },
2991                AutoComplete { AutoComplete },
2992                HasPopup { HasPopup },
2993                ListStyle { ListStyle },
2994                TextAlign { TextAlign },
2995                VerticalOffset { VerticalOffset },
2996                Affine { Transform },
2997                Rect { Bounds },
2998                TextSelection { TextSelection },
2999                CustomActionVec { CustomActions },
3000                TreeId { TreeId }
3001            });
3002        }
3003
3004        Ok(props)
3005    }
3006}
3007
3008#[cfg(feature = "serde")]
3009impl<'de> Deserialize<'de> for Properties {
3010    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3011    where
3012        D: Deserializer<'de>,
3013    {
3014        deserializer.deserialize_map(PropertiesVisitor)
3015    }
3016}
3017
3018#[cfg(feature = "schemars")]
3019macro_rules! add_schema_property {
3020    ($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
3021        let name = format!("{:?}", $enum_value);
3022        let name = name[..1].to_ascii_lowercase() + &name[1..];
3023        let subschema = $gen.subschema_for::<$type>();
3024        $properties.insert(name, SchemaValue::from(subschema));
3025    }};
3026}
3027
3028#[cfg(feature = "schemars")]
3029macro_rules! add_properties_to_schema {
3030    ($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
3031        $($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
3032    }
3033}
3034
3035#[cfg(feature = "schemars")]
3036impl JsonSchema for Properties {
3037    #[inline]
3038    fn schema_name() -> Cow<'static, str> {
3039        "Properties".into()
3040    }
3041
3042    fn json_schema(generator: &mut SchemaGenerator) -> Schema {
3043        let mut properties = SchemaMap::<String, SchemaValue>::new();
3044        add_properties_to_schema!(generator, properties, {
3045            Vec<NodeId> {
3046                Children,
3047                Controls,
3048                Details,
3049                DescribedBy,
3050                FlowTo,
3051                LabelledBy,
3052                Owns,
3053                RadioGroup
3054            },
3055            NodeId {
3056                ActiveDescendant,
3057                ErrorMessage,
3058                InPageLinkTarget,
3059                MemberOf,
3060                NextOnLine,
3061                PreviousOnLine,
3062                PopupFor
3063            },
3064            String {
3065                Label,
3066                Description,
3067                Value,
3068                AccessKey,
3069                AuthorId,
3070                ClassName,
3071                FontFamily,
3072                HtmlId,
3073                HtmlTag,
3074                InnerHtml,
3075                KeyboardShortcut,
3076                Language,
3077                Placeholder,
3078                RoleDescription,
3079                StateDescription,
3080                Tooltip,
3081                Url,
3082                RowIndexText,
3083                ColumnIndexText,
3084                BrailleLabel,
3085                BrailleRoleDescription
3086            },
3087            f64 {
3088                ScrollX,
3089                ScrollXMin,
3090                ScrollXMax,
3091                ScrollY,
3092                ScrollYMin,
3093                ScrollYMax,
3094                NumericValue,
3095                MinNumericValue,
3096                MaxNumericValue,
3097                NumericValueStep,
3098                NumericValueJump
3099            },
3100            f32 {
3101                FontSize,
3102                FontWeight
3103            },
3104            usize {
3105                RowCount,
3106                ColumnCount,
3107                RowIndex,
3108                ColumnIndex,
3109                RowSpan,
3110                ColumnSpan,
3111                Level,
3112                SizeOfSet,
3113                PositionInSet
3114            },
3115            Color {
3116                ColorValue,
3117                BackgroundColor,
3118                ForegroundColor
3119            },
3120            TextDecoration {
3121                Overline,
3122                Strikethrough,
3123                Underline
3124            },
3125            Vec<u8> {
3126                CharacterLengths,
3127                WordStarts
3128            },
3129            Vec<f32> {
3130                CharacterPositions,
3131                CharacterWidths
3132            },
3133            bool {
3134                Expanded,
3135                Selected
3136            },
3137            Invalid { Invalid },
3138            Toggled { Toggled },
3139            Live { Live },
3140            TextDirection { TextDirection },
3141            Orientation { Orientation },
3142            SortDirection { SortDirection },
3143            AriaCurrent { AriaCurrent },
3144            AutoComplete { AutoComplete },
3145            HasPopup { HasPopup },
3146            ListStyle { ListStyle },
3147            TextAlign { TextAlign },
3148            VerticalOffset { VerticalOffset },
3149            Affine { Transform },
3150            Rect { Bounds },
3151            TextSelection { TextSelection },
3152            Vec<CustomAction> { CustomActions }
3153        });
3154        json_schema!({
3155            "type": "object",
3156            "properties": properties
3157        })
3158    }
3159}
3160
3161/// The data associated with an accessibility tree that's global to the
3162/// tree and not associated with any particular node.
3163#[derive(Clone, Debug, PartialEq, Eq)]
3164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3165#[cfg_attr(feature = "schemars", derive(JsonSchema))]
3166#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
3167#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
3168pub struct TreeInfo {
3169    /// The identifier of the tree's root node.
3170    pub root: NodeId,
3171    /// The name of the UI toolkit in use.
3172    pub toolkit_name: Option<String>,
3173    /// The version of the UI toolkit.
3174    pub toolkit_version: Option<String>,
3175}
3176
3177#[deprecated(note = "Use TreeInfo instead")]
3178pub type Tree = TreeInfo;
3179
3180impl TreeInfo {
3181    #[inline]
3182    pub fn new(root: NodeId) -> TreeInfo {
3183        TreeInfo {
3184            root,
3185            toolkit_name: None,
3186            toolkit_version: None,
3187        }
3188    }
3189}
3190
3191/// A serializable representation of an atomic change to a tree.
3192///
3193/// The sender and receiver must be in sync; the update is only meant
3194/// to bring the tree from a specific previous state into its next state.
3195/// Trying to apply it to the wrong tree should immediately panic.
3196///
3197/// Note that for performance, an update should only include nodes that are
3198/// new or changed. AccessKit platform adapters will avoid raising extraneous
3199/// events for nodes that have not changed since the previous update,
3200/// but there is still a cost in processing these nodes and replacing
3201/// the previous instances.
3202#[derive(Clone, Debug, PartialEq)]
3203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3204#[cfg_attr(feature = "schemars", derive(JsonSchema))]
3205#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
3206#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
3207pub struct TreeUpdate {
3208    /// Zero or more new or updated nodes. Order doesn't matter.
3209    ///
3210    /// Each node in this list will overwrite any existing node with the same ID.
3211    /// This means that when updating a node, fields that are unchanged
3212    /// from the previous version must still be set to the same values
3213    /// as before.
3214    ///
3215    /// It is an error for any node in this list to not be either the root
3216    /// or a child of another node. For nodes other than the root, the parent
3217    /// must be either an unchanged node already in the tree, or another node
3218    /// in this list.
3219    ///
3220    /// To add a child to the tree, the list must include both the child
3221    /// and an updated version of the parent with the child's ID added to
3222    /// [`Node::children`].
3223    ///
3224    /// To remove a child and all of its descendants, this list must include
3225    /// an updated version of the parent node with the child's ID removed
3226    /// from [`Node::children`]. Neither the child nor any of its descendants
3227    /// may be included in this list.
3228    pub nodes: Vec<(NodeId, Node)>,
3229
3230    /// Rarely updated information about the tree as a whole. This may be omitted
3231    /// if it has not changed since the previous update, but providing the same
3232    /// information again is also allowed. This is required when initializing
3233    /// a tree.
3234    pub tree: Option<TreeInfo>,
3235
3236    /// The identifier of the tree that this update applies to.
3237    ///
3238    /// Use [`TreeId::ROOT`] for the main/root tree. For subtrees, use a unique
3239    /// [`TreeId`] that identifies the subtree.
3240    ///
3241    /// When updating a subtree (non-ROOT tree_id):
3242    /// - A graft node with [`Node::tree_id`] set to this tree's ID must already
3243    ///   exist in the parent tree before the first subtree update.
3244    /// - The first update for a subtree must include [`tree`](Self::tree) data.
3245    pub tree_id: TreeId,
3246
3247    /// The node within this tree that has keyboard focus when the native
3248    /// host (e.g. window) has focus. If no specific node within the tree
3249    /// has keyboard focus, this must be set to the root. The latest focus state
3250    /// must be provided with every tree update, even if the focus state
3251    /// didn't change in a given update.
3252    ///
3253    /// For subtrees, this specifies which node has focus when the subtree
3254    /// itself is focused (i.e., when focus is on the graft node in the parent
3255    /// tree).
3256    pub focus: NodeId,
3257}
3258
3259/// The amount by which to scroll in the direction specified by one of the
3260/// `Scroll` actions.
3261#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3262#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3263#[cfg_attr(feature = "schemars", derive(JsonSchema))]
3264#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
3265#[cfg_attr(
3266    feature = "pyo3",
3267    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
3268)]
3269#[repr(u8)]
3270pub enum ScrollUnit {
3271    /// A single item of a list, line of text (for vertical scrolling),
3272    /// character (for horizontal scrolling), or an approximation of
3273    /// one of these.
3274    Item,
3275    /// The amount of content that fits in the viewport.
3276    Page,
3277}
3278
3279/// A suggestion about where the node being scrolled into view should be
3280/// positioned relative to the edges of the scrollable container.
3281#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3282#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3283#[cfg_attr(feature = "schemars", derive(JsonSchema))]
3284#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
3285#[cfg_attr(
3286    feature = "pyo3",
3287    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
3288)]
3289#[repr(u8)]
3290pub enum ScrollHint {
3291    TopLeft,
3292    BottomRight,
3293    TopEdge,
3294    BottomEdge,
3295    LeftEdge,
3296    RightEdge,
3297}
3298
3299#[derive(Clone, Debug, PartialEq)]
3300#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3301#[cfg_attr(feature = "schemars", derive(JsonSchema))]
3302#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
3303#[repr(C)]
3304pub enum ActionData {
3305    CustomAction(i32),
3306    Value(Box<str>),
3307    NumericValue(f64),
3308    ScrollUnit(ScrollUnit),
3309    /// Optional suggestion for [`Action::ScrollIntoView`], specifying
3310    /// the preferred position of the target node relative to the scrollable
3311    /// container's viewport.
3312    ScrollHint(ScrollHint),
3313    /// Target for [`Action::ScrollToPoint`], in platform-native coordinates
3314    /// relative to the origin of the tree's container (e.g. window).
3315    ScrollToPoint(Point),
3316    /// Target for [`Action::SetScrollOffset`], in the coordinate space
3317    /// of the action's target node.
3318    SetScrollOffset(Point),
3319    SetTextSelection(TextSelection),
3320}
3321
3322#[derive(Clone, Debug, PartialEq)]
3323#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3324#[cfg_attr(feature = "schemars", derive(JsonSchema))]
3325#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
3326#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
3327pub struct ActionRequest {
3328    pub action: Action,
3329    pub target_tree: TreeId,
3330    pub target_node: NodeId,
3331    pub data: Option<ActionData>,
3332}
3333
3334/// Handles activation of the application's accessibility implementation.
3335pub trait ActivationHandler {
3336    /// Requests a [`TreeUpdate`] with a full tree. If the application
3337    /// can generate the tree synchronously within this method call,
3338    /// it should do so and return the [`TreeUpdate`]. Otherwise,
3339    /// it must send the update to the platform adapter asynchronously,
3340    /// no later than the next display refresh, even if a frame would not
3341    /// normally be rendered due to user input or other activity.
3342    /// The application should not return or send a placeholder [`TreeUpdate`];
3343    /// the platform adapter will provide one if necessary until the real
3344    /// tree is sent.
3345    ///
3346    /// The primary purpose of this method is to allow the application
3347    /// to lazily initialize its accessibility implementation. However,
3348    /// this method may be called consecutively without any call to
3349    /// [`DeactivationHandler::deactivate_accessibility`]; this typically happens
3350    /// if the platform adapter merely forwards tree updates to assistive
3351    /// technologies without maintaining any state. A call to this method
3352    /// must always generate a [`TreeUpdate`] with a full tree, even if
3353    /// the application normally sends incremental updates.
3354    ///
3355    /// The thread on which this method is called is platform-dependent.
3356    /// Refer to the platform adapter documentation for more details.
3357    fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
3358}
3359
3360/// Handles requests from assistive technologies or other clients.
3361pub trait ActionHandler {
3362    /// Perform the requested action. If the requested action is not supported,
3363    /// this method must do nothing.
3364    ///
3365    /// The thread on which this method is called is platform-dependent.
3366    /// Refer to the platform adapter documentation for more details.
3367    ///
3368    /// This method may queue the request and handle it asynchronously.
3369    /// This behavior is preferred over blocking, e.g. when dispatching
3370    /// the request to another thread.
3371    fn do_action(&mut self, request: ActionRequest);
3372}
3373
3374/// Handles deactivation of the application's accessibility implementation.
3375pub trait DeactivationHandler {
3376    /// Deactivate the application's accessibility implementation and drop any
3377    /// associated data that can be reconstructed later. After this method
3378    /// is called, if an accessibility tree is needed again, the platform
3379    /// adapter will call [`ActivationHandler::request_initial_tree`] again.
3380    ///
3381    /// The thread on which this method is called is platform-dependent.
3382    /// Refer to the platform adapter documentation for more details.
3383    fn deactivate_accessibility(&mut self);
3384}
3385
3386#[cfg(test)]
3387mod tests {
3388    use super::*;
3389    use alloc::format;
3390
3391    #[test]
3392    fn nodes_should_be_equal_regardless_of_the_order_properties_were_set() {
3393        let mut node = Node::new(Role::Button);
3394        node.set_label("a label");
3395        node.set_children([NodeId(1)]);
3396        node.set_description("a description");
3397
3398        let mut reordered = Node::new(Role::Button);
3399        reordered.set_description("a description");
3400        reordered.set_children([NodeId(1)]);
3401        reordered.set_label("a label");
3402
3403        assert_eq!(node, reordered);
3404    }
3405
3406    #[test]
3407    fn cleared_property_should_be_equal_to_one_that_was_never_set() {
3408        let mut node = Node::new(Role::Button);
3409        node.set_label("a label");
3410        node.clear_label();
3411
3412        assert_eq!(node, Node::new(Role::Button));
3413    }
3414
3415    #[test]
3416    fn clone_from_should_be_equivalent_to_clone() {
3417        let mut source = Node::new(Role::Button);
3418        source.add_action(Action::Click);
3419        source.add_child_action(Action::Focus);
3420        source.set_hidden();
3421        source.set_label("source");
3422        source.set_children([NodeId(1)]);
3423
3424        let mut dest = Node::new(Role::CheckBox);
3425        dest.add_action(Action::Focus);
3426        dest.set_multiselectable();
3427        dest.set_description("dest");
3428        dest.set_children([NodeId(7), NodeId(8)]);
3429        dest.clone_from(&source);
3430
3431        assert_eq!(dest, source.clone());
3432    }
3433
3434    #[test]
3435    fn clone_from_should_reuse_the_destination_string_buffer() {
3436        let mut source = Node::new(Role::Button);
3437        source.set_label("new label");
3438        let mut dest = Node::new(Role::Button);
3439        dest.set_label("old label with room to spare");
3440        let buffer = dest.label().unwrap().as_ptr();
3441
3442        dest.clone_from(&source);
3443
3444        assert_eq!(dest.label(), Some("new label"));
3445        assert_eq!(dest.label().unwrap().as_ptr(), buffer);
3446    }
3447
3448    #[test]
3449    fn clone_from_should_reuse_the_destination_slice_buffer() {
3450        let mut source = Node::new(Role::TextRun);
3451        source.set_character_lengths([1u8, 2, 3]);
3452        let mut dest = Node::new(Role::TextRun);
3453        dest.set_character_lengths([9u8; 16]);
3454        let buffer = dest.character_lengths().as_ptr();
3455
3456        dest.clone_from(&source);
3457
3458        assert_eq!(dest.character_lengths(), &[1, 2, 3]);
3459        assert_eq!(dest.character_lengths().as_ptr(), buffer);
3460    }
3461
3462    #[test]
3463    fn u64_should_be_convertible_to_node_id() {
3464        assert_eq!(NodeId::from(0u64), NodeId(0));
3465        assert_eq!(NodeId::from(1u64), NodeId(1));
3466    }
3467
3468    #[test]
3469    fn node_id_should_be_convertible_to_u64() {
3470        assert_eq!(u64::from(NodeId(0)), 0u64);
3471        assert_eq!(u64::from(NodeId(1)), 1u64);
3472    }
3473
3474    #[test]
3475    fn node_id_should_have_debug_repr() {
3476        assert_eq!(&format!("{:?}", NodeId(0)), "#0");
3477        assert_eq!(&format!("{:?}", NodeId(1)), "#1");
3478    }
3479
3480    #[test]
3481    fn action_n_should_return_the_corresponding_variant() {
3482        assert_eq!(Action::n(0), Some(Action::Click));
3483        assert_eq!(Action::n(1), Some(Action::Focus));
3484        assert_eq!(Action::n(2), Some(Action::Blur));
3485        assert_eq!(Action::n(3), Some(Action::Collapse));
3486        assert_eq!(Action::n(4), Some(Action::Expand));
3487        assert_eq!(Action::n(5), Some(Action::CustomAction));
3488        assert_eq!(Action::n(6), Some(Action::Decrement));
3489        assert_eq!(Action::n(7), Some(Action::Increment));
3490        assert_eq!(Action::n(8), Some(Action::HideTooltip));
3491        assert_eq!(Action::n(9), Some(Action::ShowTooltip));
3492        assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
3493        assert_eq!(Action::n(11), Some(Action::ScrollDown));
3494        assert_eq!(Action::n(12), Some(Action::ScrollLeft));
3495        assert_eq!(Action::n(13), Some(Action::ScrollRight));
3496        assert_eq!(Action::n(14), Some(Action::ScrollUp));
3497        assert_eq!(Action::n(15), Some(Action::ScrollIntoView));
3498        assert_eq!(Action::n(16), Some(Action::ScrollToPoint));
3499        assert_eq!(Action::n(17), Some(Action::SetScrollOffset));
3500        assert_eq!(Action::n(18), Some(Action::SetTextSelection));
3501        assert_eq!(
3502            Action::n(19),
3503            Some(Action::SetSequentialFocusNavigationStartingPoint)
3504        );
3505        assert_eq!(Action::n(20), Some(Action::SetValue));
3506        assert_eq!(Action::n(21), Some(Action::ShowContextMenu));
3507        assert_eq!(Action::n(22), None);
3508    }
3509
3510    #[test]
3511    fn empty_action_mask_should_be_converted_to_empty_vec() {
3512        assert_eq!(
3513            Vec::<Action>::new(),
3514            action_mask_to_action_vec(Node::new(Role::Unknown).actions)
3515        );
3516    }
3517
3518    #[test]
3519    fn action_mask_should_be_convertible_to_vec() {
3520        let mut node = Node::new(Role::Unknown);
3521        node.add_action(Action::Click);
3522        assert_eq!(
3523            &[Action::Click],
3524            action_mask_to_action_vec(node.actions).as_slice()
3525        );
3526
3527        let mut node = Node::new(Role::Unknown);
3528        node.add_action(Action::ShowContextMenu);
3529        assert_eq!(
3530            &[Action::ShowContextMenu],
3531            action_mask_to_action_vec(node.actions).as_slice()
3532        );
3533
3534        let mut node = Node::new(Role::Unknown);
3535        node.add_action(Action::Click);
3536        node.add_action(Action::ShowContextMenu);
3537        assert_eq!(
3538            &[Action::Click, Action::ShowContextMenu],
3539            action_mask_to_action_vec(node.actions).as_slice()
3540        );
3541
3542        let mut node = Node::new(Role::Unknown);
3543        node.add_action(Action::Focus);
3544        node.add_action(Action::Blur);
3545        node.add_action(Action::Collapse);
3546        assert_eq!(
3547            &[Action::Focus, Action::Blur, Action::Collapse],
3548            action_mask_to_action_vec(node.actions).as_slice()
3549        );
3550    }
3551
3552    #[test]
3553    fn new_node_should_have_user_provided_role() {
3554        let node = Node::new(Role::Button);
3555        assert_eq!(node.role(), Role::Button);
3556    }
3557
3558    #[test]
3559    fn node_role_setter_should_update_the_role() {
3560        let mut node = Node::new(Role::Button);
3561        node.set_role(Role::CheckBox);
3562        assert_eq!(node.role(), Role::CheckBox);
3563    }
3564
3565    macro_rules! assert_absent_action {
3566        ($node:ident, $action:ident) => {
3567            assert!(!$node.supports_action(Action::$action));
3568            assert!(!$node.child_supports_action(Action::$action));
3569        };
3570    }
3571
3572    #[test]
3573    fn new_node_should_not_support_anyaction() {
3574        let node = Node::new(Role::Unknown);
3575        assert_absent_action!(node, Click);
3576        assert_absent_action!(node, Focus);
3577        assert_absent_action!(node, Blur);
3578        assert_absent_action!(node, Collapse);
3579        assert_absent_action!(node, Expand);
3580        assert_absent_action!(node, CustomAction);
3581        assert_absent_action!(node, Decrement);
3582        assert_absent_action!(node, Increment);
3583        assert_absent_action!(node, HideTooltip);
3584        assert_absent_action!(node, ShowTooltip);
3585        assert_absent_action!(node, ReplaceSelectedText);
3586        assert_absent_action!(node, ScrollDown);
3587        assert_absent_action!(node, ScrollLeft);
3588        assert_absent_action!(node, ScrollRight);
3589        assert_absent_action!(node, ScrollUp);
3590        assert_absent_action!(node, ScrollIntoView);
3591        assert_absent_action!(node, ScrollToPoint);
3592        assert_absent_action!(node, SetScrollOffset);
3593        assert_absent_action!(node, SetTextSelection);
3594        assert_absent_action!(node, SetSequentialFocusNavigationStartingPoint);
3595        assert_absent_action!(node, SetValue);
3596        assert_absent_action!(node, ShowContextMenu);
3597    }
3598
3599    #[test]
3600    fn node_add_action_should_add_the_action() {
3601        let mut node = Node::new(Role::Unknown);
3602        node.add_action(Action::Focus);
3603        assert!(node.supports_action(Action::Focus));
3604        node.add_action(Action::Blur);
3605        assert!(node.supports_action(Action::Blur));
3606    }
3607
3608    #[test]
3609    fn node_add_child_action_should_add_the_action() {
3610        let mut node = Node::new(Role::Unknown);
3611        node.add_child_action(Action::Focus);
3612        assert!(node.child_supports_action(Action::Focus));
3613        node.add_child_action(Action::Blur);
3614        assert!(node.child_supports_action(Action::Blur));
3615    }
3616
3617    #[test]
3618    fn node_add_action_should_do_nothing_if_the_action_is_already_supported() {
3619        let mut node = Node::new(Role::Unknown);
3620        node.add_action(Action::Focus);
3621        node.add_action(Action::Focus);
3622        assert!(node.supports_action(Action::Focus));
3623    }
3624
3625    #[test]
3626    fn node_add_child_action_should_do_nothing_if_the_action_is_already_supported() {
3627        let mut node = Node::new(Role::Unknown);
3628        node.add_child_action(Action::Focus);
3629        node.add_child_action(Action::Focus);
3630        assert!(node.child_supports_action(Action::Focus));
3631    }
3632
3633    #[test]
3634    fn node_remove_action_should_remove_the_action() {
3635        let mut node = Node::new(Role::Unknown);
3636        node.add_action(Action::Blur);
3637        node.remove_action(Action::Blur);
3638        assert!(!node.supports_action(Action::Blur));
3639    }
3640
3641    #[test]
3642    fn node_remove_child_action_should_remove_the_action() {
3643        let mut node = Node::new(Role::Unknown);
3644        node.add_child_action(Action::Blur);
3645        node.remove_child_action(Action::Blur);
3646        assert!(!node.child_supports_action(Action::Blur));
3647    }
3648
3649    #[test]
3650    fn node_clear_actions_should_remove_all_actions() {
3651        let mut node = Node::new(Role::Unknown);
3652        node.add_action(Action::Focus);
3653        node.add_action(Action::Blur);
3654        node.clear_actions();
3655        assert!(!node.supports_action(Action::Focus));
3656        assert!(!node.supports_action(Action::Blur));
3657    }
3658
3659    #[test]
3660    fn node_clear_child_actions_should_remove_all_actions() {
3661        let mut node = Node::new(Role::Unknown);
3662        node.add_child_action(Action::Focus);
3663        node.add_child_action(Action::Blur);
3664        node.clear_child_actions();
3665        assert!(!node.child_supports_action(Action::Focus));
3666        assert!(!node.child_supports_action(Action::Blur));
3667    }
3668
3669    #[test]
3670    fn node_should_have_debug_repr() {
3671        let mut node = Node::new(Role::Unknown);
3672        node.add_action(Action::Click);
3673        node.add_action(Action::Focus);
3674        node.add_child_action(Action::ScrollIntoView);
3675        node.set_hidden();
3676        node.set_multiselectable();
3677        node.set_children([NodeId(0), NodeId(1)]);
3678        node.set_active_descendant(NodeId(2));
3679        node.push_custom_action(CustomAction {
3680            id: 0,
3681            description: "test action".into(),
3682        });
3683
3684        assert_eq!(
3685            &format!("{node:?}"),
3686            r#"Node { role: Unknown, actions: [Click, Focus], child_actions: [ScrollIntoView], is_hidden: true, is_multiselectable: true, children: [#0, #1], active_descendant: #2, custom_actions: [CustomAction { id: 0, description: "test action" }] }"#
3687        );
3688    }
3689
3690    #[test]
3691    fn new_tree_should_have_root_id() {
3692        let tree = TreeInfo::new(NodeId(1));
3693        assert_eq!(tree.root, NodeId(1));
3694        assert_eq!(tree.toolkit_name, None);
3695        assert_eq!(tree.toolkit_version, None);
3696    }
3697}