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::{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: Box<str>,
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(Clone, Debug, PartialEq)]
856enum PropertyValue {
857    None,
858    NodeIdVec(Vec<NodeId>),
859    NodeId(NodeId),
860    String(Box<str>),
861    F64(f64),
862    F32(f32),
863    Usize(usize),
864    Color(Color),
865    TextDecoration(TextDecoration),
866    LengthSlice(Box<[u8]>),
867    CoordSlice(Box<[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    HtmlTag,
922    InnerHtml,
923    KeyboardShortcut,
924    Language,
925    Placeholder,
926    RoleDescription,
927    StateDescription,
928    Tooltip,
929    Url,
930    RowIndexText,
931    ColumnIndexText,
932    BrailleLabel,
933    BrailleRoleDescription,
934
935    // f64
936    ScrollX,
937    ScrollXMin,
938    ScrollXMax,
939    ScrollY,
940    ScrollYMin,
941    ScrollYMax,
942    NumericValue,
943    MinNumericValue,
944    MaxNumericValue,
945    NumericValueStep,
946    NumericValueJump,
947
948    // f32
949    FontSize,
950    FontWeight,
951
952    // usize
953    RowCount,
954    ColumnCount,
955    RowIndex,
956    ColumnIndex,
957    RowSpan,
958    ColumnSpan,
959    Level,
960    SizeOfSet,
961    PositionInSet,
962
963    // Color
964    ColorValue,
965    BackgroundColor,
966    ForegroundColor,
967
968    // TextDecoration
969    Overline,
970    Strikethrough,
971    Underline,
972
973    // LengthSlice
974    CharacterLengths,
975    WordStarts,
976
977    // CoordSlice
978    CharacterPositions,
979    CharacterWidths,
980
981    // bool
982    Expanded,
983    Selected,
984
985    // Unique enums
986    Invalid,
987    Toggled,
988    Live,
989    TextDirection,
990    Orientation,
991    SortDirection,
992    AriaCurrent,
993    AutoComplete,
994    HasPopup,
995    ListStyle,
996    TextAlign,
997    VerticalOffset,
998
999    // Other
1000    Transform,
1001    Bounds,
1002    TextSelection,
1003    CustomActions,
1004    TreeId,
1005
1006    // This MUST be last.
1007    Unset,
1008}
1009
1010#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1011#[repr(transparent)]
1012struct PropertyIndices([u8; PropertyId::Unset as usize]);
1013
1014impl Default for PropertyIndices {
1015    fn default() -> Self {
1016        Self([PropertyId::Unset as u8; PropertyId::Unset as usize])
1017    }
1018}
1019
1020#[derive(Clone, Debug, Default, PartialEq)]
1021struct Properties {
1022    indices: PropertyIndices,
1023    values: Vec<PropertyValue>,
1024}
1025
1026/// A single accessible object. A complete UI is represented as a tree of these.
1027///
1028/// For brevity, and to make more of the documentation usable in bindings
1029/// to other languages, documentation of getter methods is written as if
1030/// documenting fields in a struct, and such methods are referred to
1031/// as properties.
1032#[derive(Clone, Default, PartialEq)]
1033#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1034#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1035#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
1036#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1037pub struct Node {
1038    role: Role,
1039    actions: u32,
1040    child_actions: u32,
1041    flags: u32,
1042    properties: Properties,
1043}
1044
1045impl PropertyIndices {
1046    fn get<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a PropertyValue {
1047        let index = self.0[id as usize];
1048        if index == PropertyId::Unset as u8 {
1049            &PropertyValue::None
1050        } else {
1051            &values[index as usize]
1052        }
1053    }
1054}
1055
1056impl Properties {
1057    fn get_mut(&mut self, id: PropertyId, default: PropertyValue) -> &mut PropertyValue {
1058        let index = self.indices.0[id as usize] as usize;
1059        if index == PropertyId::Unset as usize {
1060            self.values.push(default);
1061            let index = self.values.len() - 1;
1062            self.indices.0[id as usize] = index as u8;
1063            &mut self.values[index]
1064        } else {
1065            &mut self.values[index]
1066        }
1067    }
1068
1069    fn set(&mut self, id: PropertyId, value: PropertyValue) {
1070        let index = self.indices.0[id as usize];
1071        if index == PropertyId::Unset as u8 {
1072            self.values.push(value);
1073            self.indices.0[id as usize] = (self.values.len() - 1) as u8;
1074        } else {
1075            self.values[index as usize] = value;
1076        }
1077    }
1078
1079    fn clear(&mut self, id: PropertyId) {
1080        let index = self.indices.0[id as usize];
1081        if index != PropertyId::Unset as u8 {
1082            self.values[index as usize] = PropertyValue::None;
1083        }
1084    }
1085}
1086
1087macro_rules! flag_methods {
1088    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1089        impl Node {
1090            $($(#[$doc])*
1091            #[inline]
1092            pub fn $getter(&self) -> bool {
1093                (self.flags & (Flag::$id).mask()) != 0
1094            }
1095            #[inline]
1096            pub fn $setter(&mut self) {
1097                self.flags |= (Flag::$id).mask();
1098            }
1099            #[inline]
1100            pub fn $clearer(&mut self) {
1101                self.flags &= !((Flag::$id).mask());
1102            })*
1103            fn debug_flag_properties(&self, fmt: &mut fmt::DebugStruct) {
1104                $(
1105                    if self.$getter() {
1106                        fmt.field(stringify!($getter), &true);
1107                    }
1108                )*
1109            }
1110        }
1111        $(#[cfg(test)]
1112        mod $getter {
1113            use super::{Node, Role};
1114
1115            #[test]
1116            fn getter_should_return_default_value() {
1117                let node = Node::new(Role::Unknown);
1118                assert!(!node.$getter());
1119            }
1120
1121            #[test]
1122            fn setter_should_update_the_property() {
1123                let mut node = Node::new(Role::Unknown);
1124                node.$setter();
1125                assert!(node.$getter());
1126            }
1127
1128            #[test]
1129            fn clearer_should_reset_the_property() {
1130                let mut node = Node::new(Role::Unknown);
1131                node.$setter();
1132                node.$clearer();
1133                assert!(!node.$getter());
1134            }
1135        })*
1136    }
1137}
1138
1139macro_rules! option_ref_type_getters {
1140    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1141        impl PropertyIndices {
1142            $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> Option<&'a $type> {
1143                match self.get(values, id) {
1144                    PropertyValue::$variant(value) => Some(value),
1145                    _ => None,
1146                }
1147            })*
1148        }
1149    }
1150}
1151
1152macro_rules! slice_type_getters {
1153    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1154        impl PropertyIndices {
1155            $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a [$type] {
1156                match self.get(values, id) {
1157                    PropertyValue::$variant(value) => value,
1158                    _ => &[],
1159                }
1160            })*
1161        }
1162    }
1163}
1164
1165macro_rules! copy_type_getters {
1166    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1167        impl PropertyIndices {
1168            $(fn $method(&self, values: &[PropertyValue], id: PropertyId) -> Option<$type> {
1169                match self.get(values, id) {
1170                    PropertyValue::$variant(value) => Some(*value),
1171                    _ => None,
1172                }
1173            })*
1174        }
1175    }
1176}
1177
1178macro_rules! box_type_setters {
1179    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1180        impl Node {
1181            $(fn $method(&mut self, id: PropertyId, value: impl Into<Box<$type>>) {
1182                self.properties.set(id, PropertyValue::$variant(value.into()));
1183            })*
1184        }
1185    }
1186}
1187
1188macro_rules! copy_type_setters {
1189    ($(($method:ident, $type:ty, $variant:ident)),+) => {
1190        impl Node {
1191            $(fn $method(&mut self, id: PropertyId, value: $type) {
1192                self.properties.set(id, PropertyValue::$variant(value));
1193            })*
1194        }
1195    }
1196}
1197
1198macro_rules! vec_type_methods {
1199    ($(($type:ty, $variant:ident, $getter:ident, $setter:ident, $pusher:ident)),+) => {
1200        $(slice_type_getters! {
1201            ($getter, $type, $variant)
1202        })*
1203        impl Node {
1204            $(fn $setter(&mut self, id: PropertyId, value: impl Into<Vec<$type>>) {
1205                self.properties.set(id, PropertyValue::$variant(value.into()));
1206            }
1207            fn $pusher(&mut self, id: PropertyId, item: $type) {
1208                if let PropertyValue::$variant(v) = self.properties.get_mut(id, PropertyValue::$variant(Vec::new())) {
1209                    v.push(item);
1210                }
1211            })*
1212        }
1213    }
1214}
1215
1216macro_rules! property_methods {
1217    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $type_getter:ident, $getter_result:ty, $setter:ident, $type_setter:ident, $setter_param:ty, $clearer:ident)),+) => {
1218        impl Node {
1219            $($(#[$doc])*
1220            #[inline]
1221            pub fn $getter(&self) -> $getter_result {
1222                self.properties.indices.$type_getter(&self.properties.values, PropertyId::$id)
1223            }
1224            #[inline]
1225            pub fn $setter(&mut self, value: $setter_param) {
1226                self.$type_setter(PropertyId::$id, value);
1227            }
1228            #[inline]
1229            pub fn $clearer(&mut self) {
1230                self.properties.clear(PropertyId::$id);
1231            })*
1232        }
1233    }
1234}
1235
1236macro_rules! vec_property_methods {
1237    ($($(#[$doc:meta])* ($id:ident, $item_type:ty, $getter:ident, $type_getter:ident, $setter:ident, $type_setter:ident, $pusher:ident, $type_pusher:ident, $clearer:ident)),+) => {
1238        $(property_methods! {
1239            $(#[$doc])*
1240            ($id, $getter, $type_getter, &[$item_type], $setter, $type_setter, impl Into<Vec<$item_type>>, $clearer)
1241        }
1242        impl Node {
1243            #[inline]
1244            pub fn $pusher(&mut self, item: $item_type) {
1245                self.$type_pusher(PropertyId::$id, item);
1246            }
1247        })*
1248    }
1249}
1250
1251macro_rules! slice_properties_debug_method {
1252    ($name:ident, [$($getter:ident,)*]) => {
1253        fn $name(&self, fmt: &mut fmt::DebugStruct) {
1254            $(
1255                let value = self.$getter();
1256                if !value.is_empty() {
1257                    fmt.field(stringify!($getter), &value);
1258                }
1259            )*
1260        }
1261    }
1262}
1263
1264macro_rules! node_id_vec_property_methods {
1265    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $pusher:ident, $clearer:ident)),+) => {
1266        $(vec_property_methods! {
1267            $(#[$doc])*
1268            ($id, NodeId, $getter, get_node_id_vec, $setter, set_node_id_vec, $pusher, push_to_node_id_vec, $clearer)
1269        })*
1270        impl Node {
1271            slice_properties_debug_method! { debug_node_id_vec_properties, [$($getter,)*] }
1272        }
1273        $(#[cfg(test)]
1274        mod $getter {
1275            use super::{Node, NodeId, Role};
1276
1277            #[test]
1278            fn getter_should_return_default_value() {
1279                let node = Node::new(Role::Unknown);
1280                assert!(node.$getter().is_empty());
1281            }
1282            #[test]
1283            fn setter_should_update_the_property() {
1284                let mut node = Node::new(Role::Unknown);
1285                node.$setter([]);
1286                assert!(node.$getter().is_empty());
1287                node.$setter([NodeId(0), NodeId(1)]);
1288                assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1289            }
1290            #[test]
1291            fn pusher_should_update_the_property() {
1292                let mut node = Node::new(Role::Unknown);
1293                node.$pusher(NodeId(0));
1294                assert_eq!(node.$getter(), &[NodeId(0)]);
1295                node.$pusher(NodeId(1));
1296                assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1297            }
1298            #[test]
1299            fn clearer_should_reset_the_property() {
1300                let mut node = Node::new(Role::Unknown);
1301                node.$setter([NodeId(0)]);
1302                node.$clearer();
1303                assert!(node.$getter().is_empty());
1304            }
1305        })*
1306    }
1307}
1308
1309macro_rules! option_properties_debug_method {
1310    ($name:ident, [$($getter:ident,)*]) => {
1311        fn $name(&self, fmt: &mut fmt::DebugStruct) {
1312            $(
1313                if let Some(value) = self.$getter() {
1314                    fmt.field(stringify!($getter), &value);
1315                }
1316            )*
1317        }
1318    }
1319}
1320
1321macro_rules! node_id_property_methods {
1322    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1323        $(property_methods! {
1324            $(#[$doc])*
1325            ($id, $getter, get_node_id_property, Option<NodeId>, $setter, set_node_id_property, NodeId, $clearer)
1326        })*
1327        impl Node {
1328            option_properties_debug_method! { debug_node_id_properties, [$($getter,)*] }
1329        }
1330        $(#[cfg(test)]
1331        mod $getter {
1332            use super::{Node, NodeId, Role};
1333
1334            #[test]
1335            fn getter_should_return_default_value() {
1336                let node = Node::new(Role::Unknown);
1337                assert!(node.$getter().is_none());
1338            }
1339            #[test]
1340            fn setter_should_update_the_property() {
1341                let mut node = Node::new(Role::Unknown);
1342                node.$setter(NodeId(1));
1343                assert_eq!(node.$getter(), Some(NodeId(1)));
1344            }
1345            #[test]
1346            fn clearer_should_reset_the_property() {
1347                let mut node = Node::new(Role::Unknown);
1348                node.$setter(NodeId(1));
1349                node.$clearer();
1350                assert!(node.$getter().is_none());
1351            }
1352        })*
1353    }
1354}
1355
1356macro_rules! string_property_methods {
1357    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1358        $(property_methods! {
1359            $(#[$doc])*
1360            ($id, $getter, get_string_property, Option<&str>, $setter, set_string_property, impl Into<Box<str>>, $clearer)
1361        })*
1362        impl Node {
1363            option_properties_debug_method! { debug_string_properties, [$($getter,)*] }
1364        }
1365        $(#[cfg(test)]
1366        mod $getter {
1367            use super::{Node, Role};
1368
1369            #[test]
1370            fn getter_should_return_default_value() {
1371                let node = Node::new(Role::Unknown);
1372                assert!(node.$getter().is_none());
1373            }
1374            #[test]
1375            fn setter_should_update_the_property() {
1376                let mut node = Node::new(Role::Unknown);
1377                node.$setter("test");
1378                assert_eq!(node.$getter(), Some("test"));
1379            }
1380            #[test]
1381            fn clearer_should_reset_the_property() {
1382                let mut node = Node::new(Role::Unknown);
1383                node.$setter("test");
1384                node.$clearer();
1385                assert!(node.$getter().is_none());
1386            }
1387        })*
1388    }
1389}
1390
1391macro_rules! f64_property_methods {
1392    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1393        $(property_methods! {
1394            $(#[$doc])*
1395            ($id, $getter, get_f64_property, Option<f64>, $setter, set_f64_property, f64, $clearer)
1396        })*
1397        impl Node {
1398            option_properties_debug_method! { debug_f64_properties, [$($getter,)*] }
1399        }
1400        $(#[cfg(test)]
1401        mod $getter {
1402            use super::{Node, Role};
1403
1404            #[test]
1405            fn getter_should_return_default_value() {
1406                let node = Node::new(Role::Unknown);
1407                assert!(node.$getter().is_none());
1408            }
1409            #[test]
1410            fn setter_should_update_the_property() {
1411                let mut node = Node::new(Role::Unknown);
1412                node.$setter(1.0);
1413                assert_eq!(node.$getter(), Some(1.0));
1414            }
1415            #[test]
1416            fn clearer_should_reset_the_property() {
1417                let mut node = Node::new(Role::Unknown);
1418                node.$setter(1.0);
1419                node.$clearer();
1420                assert!(node.$getter().is_none());
1421            }
1422        })*
1423    }
1424}
1425
1426macro_rules! f32_property_methods {
1427    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1428        $(property_methods! {
1429            $(#[$doc])*
1430            ($id, $getter, get_f32_property, Option<f32>, $setter, set_f32_property, f32, $clearer)
1431        })*
1432        impl Node {
1433            option_properties_debug_method! { debug_f32_properties, [$($getter,)*] }
1434        }
1435        $(#[cfg(test)]
1436        mod $getter {
1437            use super::{Node, Role};
1438
1439            #[test]
1440            fn getter_should_return_default_value() {
1441                let node = Node::new(Role::Unknown);
1442                assert!(node.$getter().is_none());
1443            }
1444            #[test]
1445            fn setter_should_update_the_property() {
1446                let mut node = Node::new(Role::Unknown);
1447                node.$setter(1.0);
1448                assert_eq!(node.$getter(), Some(1.0));
1449            }
1450            #[test]
1451            fn clearer_should_reset_the_property() {
1452                let mut node = Node::new(Role::Unknown);
1453                node.$setter(1.0);
1454                node.$clearer();
1455                assert!(node.$getter().is_none());
1456            }
1457        })*
1458    }
1459}
1460
1461macro_rules! usize_property_methods {
1462    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1463        $(property_methods! {
1464            $(#[$doc])*
1465            ($id, $getter, get_usize_property, Option<usize>, $setter, set_usize_property, usize, $clearer)
1466        })*
1467        impl Node {
1468            option_properties_debug_method! { debug_usize_properties, [$($getter,)*] }
1469        }
1470        $(#[cfg(test)]
1471        mod $getter {
1472            use super::{Node, Role};
1473
1474            #[test]
1475            fn getter_should_return_default_value() {
1476                let node = Node::new(Role::Unknown);
1477                assert!(node.$getter().is_none());
1478            }
1479            #[test]
1480            fn setter_should_update_the_property() {
1481                let mut node = Node::new(Role::Unknown);
1482                node.$setter(1);
1483                assert_eq!(node.$getter(), Some(1));
1484            }
1485            #[test]
1486            fn clearer_should_reset_the_property() {
1487                let mut node = Node::new(Role::Unknown);
1488                node.$setter(1);
1489                node.$clearer();
1490                assert!(node.$getter().is_none());
1491            }
1492        })*
1493    }
1494}
1495
1496macro_rules! color_property_methods {
1497    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1498        $(property_methods! {
1499            $(#[$doc])*
1500            ($id, $getter, get_color_property, Option<Color>, $setter, set_color_property, Color, $clearer)
1501        })*
1502        impl Node {
1503            option_properties_debug_method! { debug_color_properties, [$($getter,)*] }
1504        }
1505        $(#[cfg(test)]
1506        mod $getter {
1507            use super::{Color, Node, Role};
1508
1509            #[test]
1510            fn getter_should_return_default_value() {
1511                let node = Node::new(Role::Unknown);
1512                assert!(node.$getter().is_none());
1513            }
1514            #[test]
1515            fn setter_should_update_the_property() {
1516                let mut node = Node::new(Role::Unknown);
1517                node.$setter(Color { red: 255, green: 255, blue: 255, alpha: 255 });
1518                assert_eq!(node.$getter(), Some(Color { red: 255, green: 255, blue: 255, alpha: 255 }));
1519            }
1520            #[test]
1521            fn clearer_should_reset_the_property() {
1522                let mut node = Node::new(Role::Unknown);
1523                node.$setter(Color { red: 255, green: 255, blue: 255, alpha: 255 });
1524                node.$clearer();
1525                assert!(node.$getter().is_none());
1526            }
1527        })*
1528    }
1529}
1530
1531macro_rules! text_decoration_property_methods {
1532    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1533        $(property_methods! {
1534            $(#[$doc])*
1535            ($id, $getter, get_text_decoration_property, Option<TextDecoration>, $setter, set_text_decoration_property, TextDecoration, $clearer)
1536        })*
1537        impl Node {
1538            option_properties_debug_method! { debug_text_decoration_properties, [$($getter,)*] }
1539        }
1540        $(#[cfg(test)]
1541        mod $getter {
1542            use super::{Color, Node, Role, TextDecoration, TextDecorationStyle};
1543
1544            const TEST_TEXT_DECORATION: TextDecoration = TextDecoration {
1545                style: TextDecorationStyle::Dotted,
1546                color: Color {
1547                    red: 0,
1548                    green: 0,
1549                    blue: 0,
1550                    alpha: 255,
1551                },
1552            };
1553
1554            #[test]
1555            fn getter_should_return_default_value() {
1556                let node = Node::new(Role::Unknown);
1557                assert!(node.$getter().is_none());
1558            }
1559            #[test]
1560            fn setter_should_update_the_property() {
1561                let mut node = Node::new(Role::Unknown);
1562                node.$setter(TEST_TEXT_DECORATION);
1563                assert_eq!(node.$getter(), Some(TEST_TEXT_DECORATION));
1564            }
1565            #[test]
1566            fn clearer_should_reset_the_property() {
1567                let mut node = Node::new(Role::Unknown);
1568                node.$setter(TEST_TEXT_DECORATION);
1569                node.$clearer();
1570                assert!(node.$getter().is_none());
1571            }
1572        })*
1573    }
1574}
1575
1576macro_rules! length_slice_property_methods {
1577    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1578        $(property_methods! {
1579            $(#[$doc])*
1580            ($id, $getter, get_length_slice_property, &[u8], $setter, set_length_slice_property, impl Into<Box<[u8]>>, $clearer)
1581        })*
1582        impl Node {
1583            slice_properties_debug_method! { debug_length_slice_properties, [$($getter,)*] }
1584        }
1585        $(#[cfg(test)]
1586        mod $getter {
1587            use super::{Node, Role};
1588
1589            #[test]
1590            fn getter_should_return_default_value() {
1591                let node = Node::new(Role::Unknown);
1592                assert!(node.$getter().is_empty());
1593            }
1594            #[test]
1595            fn setter_should_update_the_property() {
1596                let mut node = Node::new(Role::Unknown);
1597                node.$setter([]);
1598                assert!(node.$getter().is_empty());
1599                node.$setter([1, 2]);
1600                assert_eq!(node.$getter(), &[1, 2]);
1601            }
1602            #[test]
1603            fn clearer_should_reset_the_property() {
1604                let mut node = Node::new(Role::Unknown);
1605                node.$setter([1, 2]);
1606                node.$clearer();
1607                assert!(node.$getter().is_empty());
1608            }
1609        })*
1610    }
1611}
1612
1613macro_rules! coord_slice_property_methods {
1614    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1615        $(property_methods! {
1616            $(#[$doc])*
1617            ($id, $getter, get_coord_slice_property, Option<&[f32]>, $setter, set_coord_slice_property, impl Into<Box<[f32]>>, $clearer)
1618        })*
1619        impl Node {
1620            option_properties_debug_method! { debug_coord_slice_properties, [$($getter,)*] }
1621        }
1622        $(#[cfg(test)]
1623        mod $getter {
1624            use super::{Node, Role};
1625
1626            #[test]
1627            fn getter_should_return_default_value() {
1628                let node = Node::new(Role::Unknown);
1629                assert!(node.$getter().is_none());
1630            }
1631            #[test]
1632            fn setter_should_update_the_property() {
1633                let mut node = Node::new(Role::Unknown);
1634                node.$setter([]);
1635                let expected: Option<&[f32]> = Some(&[]);
1636                assert_eq!(node.$getter(), expected);
1637                node.$setter([1.0, 2.0]);
1638                let expected: Option<&[f32]> = Some(&[1.0, 2.0]);
1639                assert_eq!(node.$getter(), expected);
1640            }
1641            #[test]
1642            fn clearer_should_reset_the_property() {
1643                let mut node = Node::new(Role::Unknown);
1644                node.$setter([1.0, 2.0]);
1645                node.$clearer();
1646                assert!(node.$getter().is_none());
1647            }
1648        })*
1649    }
1650}
1651
1652macro_rules! bool_property_methods {
1653    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1654        $(property_methods! {
1655            $(#[$doc])*
1656            ($id, $getter, get_bool_property, Option<bool>, $setter, set_bool_property, bool, $clearer)
1657        })*
1658        impl Node {
1659            option_properties_debug_method! { debug_bool_properties, [$($getter,)*] }
1660        }
1661        $(#[cfg(test)]
1662        mod $getter {
1663            use super::{Node, Role};
1664
1665            #[test]
1666            fn getter_should_return_default_value() {
1667                let node = Node::new(Role::Unknown);
1668                assert!(node.$getter().is_none());
1669            }
1670            #[test]
1671            fn setter_should_update_the_property() {
1672                let mut node = Node::new(Role::Unknown);
1673                node.$setter(true);
1674                assert_eq!(node.$getter(), Some(true));
1675            }
1676            #[test]
1677            fn clearer_should_reset_the_property() {
1678                let mut node = Node::new(Role::Unknown);
1679                node.$setter(true);
1680                node.$clearer();
1681                assert!(node.$getter().is_none());
1682            }
1683        })*
1684    }
1685}
1686
1687macro_rules! unique_enum_property_methods {
1688    ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident, $variant:ident)),+) => {
1689        impl Node {
1690            $($(#[$doc])*
1691            #[inline]
1692            pub fn $getter(&self) -> Option<$id> {
1693                match self.properties.indices.get(&self.properties.values, PropertyId::$id) {
1694                    PropertyValue::$id(value) => Some(*value),
1695                    _ => None,
1696                }
1697            }
1698            #[inline]
1699            pub fn $setter(&mut self, value: $id) {
1700                self.properties.set(PropertyId::$id, PropertyValue::$id(value));
1701            }
1702            #[inline]
1703            pub fn $clearer(&mut self) {
1704                self.properties.clear(PropertyId::$id);
1705            })*
1706            option_properties_debug_method! { debug_unique_enum_properties, [$($getter,)*] }
1707        }
1708        $(#[cfg(test)]
1709        mod $getter {
1710            use super::{Node, Role};
1711
1712            #[test]
1713            fn getter_should_return_default_value() {
1714                let node = Node::new(Role::Unknown);
1715                assert!(node.$getter().is_none());
1716            }
1717            #[test]
1718            fn setter_should_update_the_property() {
1719                let mut node = Node::new(Role::Unknown);
1720                let variant = super::$id::$variant;
1721                node.$setter(variant);
1722                assert_eq!(node.$getter(), Some(variant));
1723            }
1724            #[test]
1725            fn clearer_should_reset_the_property() {
1726                let mut node = Node::new(Role::Unknown);
1727                node.$setter(super::$id::$variant);
1728                node.$clearer();
1729                assert!(node.$getter().is_none());
1730            }
1731        })*
1732    }
1733}
1734
1735impl Node {
1736    #[inline]
1737    pub fn new(role: Role) -> Self {
1738        Self {
1739            role,
1740            ..Default::default()
1741        }
1742    }
1743}
1744
1745impl Node {
1746    #[inline]
1747    pub fn role(&self) -> Role {
1748        self.role
1749    }
1750    #[inline]
1751    pub fn set_role(&mut self, value: Role) {
1752        self.role = value;
1753    }
1754
1755    #[inline]
1756    pub fn supports_action(&self, action: Action) -> bool {
1757        (self.actions & action.mask()) != 0
1758    }
1759    #[inline]
1760    pub fn add_action(&mut self, action: Action) {
1761        self.actions |= action.mask();
1762    }
1763    #[inline]
1764    pub fn remove_action(&mut self, action: Action) {
1765        self.actions &= !(action.mask());
1766    }
1767    #[inline]
1768    pub fn clear_actions(&mut self) {
1769        self.actions = 0;
1770    }
1771
1772    /// Return whether the specified action is in the set supported on this node's
1773    /// direct children in the filtered tree.
1774    #[inline]
1775    pub fn child_supports_action(&self, action: Action) -> bool {
1776        (self.child_actions & action.mask()) != 0
1777    }
1778    /// Add the specified action to the set supported on this node's direct
1779    /// children in the filtered tree.
1780    #[inline]
1781    pub fn add_child_action(&mut self, action: Action) {
1782        self.child_actions |= action.mask();
1783    }
1784    /// Remove the specified action from the set supported on this node's direct
1785    /// children in the filtered tree.
1786    #[inline]
1787    pub fn remove_child_action(&mut self, action: Action) {
1788        self.child_actions &= !(action.mask());
1789    }
1790    /// Clear the set of actions supported on this node's direct children in the
1791    /// filtered tree.
1792    #[inline]
1793    pub fn clear_child_actions(&mut self) {
1794        self.child_actions = 0;
1795    }
1796}
1797
1798flag_methods! {
1799    /// Exclude this node and its descendants from the tree presented to
1800    /// assistive technologies, and from hit testing.
1801    (Hidden, is_hidden, set_hidden, clear_hidden),
1802    (Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
1803    (Required, is_required, set_required, clear_required),
1804    (Visited, is_visited, set_visited, clear_visited),
1805    (Busy, is_busy, set_busy, clear_busy),
1806    (LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
1807    /// If a dialog box is marked as explicitly modal.
1808    (Modal, is_modal, set_modal, clear_modal),
1809    /// This element allows touches to be passed through when a screen reader
1810    /// is in touch exploration mode, e.g. a virtual keyboard normally
1811    /// behaves this way.
1812    (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1813    /// Use for a text widget that allows focus/selection but not input.
1814    (ReadOnly, is_read_only, set_read_only, clear_read_only),
1815    /// Use for a control or group of controls that disallows input.
1816    (Disabled, is_disabled, set_disabled, clear_disabled),
1817    (Italic, is_italic, set_italic, clear_italic),
1818    /// Indicates that this node clips its children, i.e. may have
1819    /// `overflow: hidden` or clip children by default.
1820    (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1821    /// Indicates whether this node causes a hard line-break
1822    /// (e.g. block level elements, or `<br>`).
1823    (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1824    /// Indicates whether this node causes a page break.
1825    (IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
1826    (IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
1827    (IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
1828    (IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
1829    (IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
1830}
1831
1832option_ref_type_getters! {
1833    (get_affine_property, Affine, Affine),
1834    (get_string_property, str, String),
1835    (get_coord_slice_property, [f32], CoordSlice),
1836    (get_text_selection_property, TextSelection, TextSelection)
1837}
1838
1839slice_type_getters! {
1840    (get_length_slice_property, u8, LengthSlice)
1841}
1842
1843copy_type_getters! {
1844    (get_rect_property, Rect, Rect),
1845    (get_node_id_property, NodeId, NodeId),
1846    (get_f64_property, f64, F64),
1847    (get_f32_property, f32, F32),
1848    (get_usize_property, usize, Usize),
1849    (get_color_property, Color, Color),
1850    (get_text_decoration_property, TextDecoration, TextDecoration),
1851    (get_bool_property, bool, Bool),
1852    (get_tree_id_property, TreeId, TreeId)
1853}
1854
1855box_type_setters! {
1856    (set_affine_property, Affine, Affine),
1857    (set_string_property, str, String),
1858    (set_length_slice_property, [u8], LengthSlice),
1859    (set_coord_slice_property, [f32], CoordSlice),
1860    (set_text_selection_property, TextSelection, TextSelection)
1861}
1862
1863copy_type_setters! {
1864    (set_rect_property, Rect, Rect),
1865    (set_node_id_property, NodeId, NodeId),
1866    (set_f64_property, f64, F64),
1867    (set_f32_property, f32, F32),
1868    (set_usize_property, usize, Usize),
1869    (set_color_property, Color, Color),
1870    (set_text_decoration_property, TextDecoration, TextDecoration),
1871    (set_bool_property, bool, Bool),
1872    (set_tree_id_property, TreeId, TreeId)
1873}
1874
1875vec_type_methods! {
1876    (NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
1877    (CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
1878}
1879
1880node_id_vec_property_methods! {
1881    (Children, children, set_children, push_child, clear_children),
1882    (Controls, controls, set_controls, push_controlled, clear_controls),
1883    (Details, details, set_details, push_detail, clear_details),
1884    (DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
1885    (FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
1886    (LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
1887    /// As with the `aria-owns` property in ARIA, this property should be set
1888    /// only if the nodes referenced in the property are not descendants
1889    /// of the owning node in the AccessKit tree. In the common case, where the
1890    /// owned nodes are direct children or indirect descendants, this property
1891    /// is unnecessary.
1892    (Owns, owns, set_owns, push_owned, clear_owns),
1893    /// On radio buttons this should be set to a list of all of the buttons
1894    /// in the same group as this one, including this radio button itself.
1895    (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
1896}
1897
1898node_id_property_methods! {
1899    /// For a composite widget such as a listbox, tree, or grid, identifies
1900    /// the currently active descendant. Used when focus remains on the container
1901    /// while the active item changes.
1902    (ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
1903    (ErrorMessage, error_message, set_error_message, clear_error_message),
1904    (InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
1905    (MemberOf, member_of, set_member_of, clear_member_of),
1906    (NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
1907    (PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
1908    (PopupFor, popup_for, set_popup_for, clear_popup_for)
1909}
1910
1911string_property_methods! {
1912    /// The label of a control that can have a label. If the label is specified
1913    /// via the [`Node::labelled_by`] relation, this doesn't need to be set.
1914    /// Note that the text content of a node with the [`Role::Label`] role
1915    /// should be provided via [`Node::value`], not this property.
1916    (Label, label, set_label, clear_label),
1917    (Description, description, set_description, clear_description),
1918    (Value, value, set_value, clear_value),
1919    /// A single character, usually part of this node's name, that can be pressed,
1920    /// possibly along with a platform-specific modifier, to perform
1921    /// this node's default action. For menu items, the access key is only active
1922    /// while the menu is active, in contrast with [`keyboard_shortcut`];
1923    /// a single menu item may in fact have both properties.
1924    ///
1925    /// [`keyboard_shortcut`]: Node::keyboard_shortcut
1926    (AccessKey, access_key, set_access_key, clear_access_key),
1927    /// A way for application authors to identify this node for automated
1928    /// testing purpose. The value must be unique among this node's siblings.
1929    (AuthorId, author_id, set_author_id, clear_author_id),
1930    (ClassName, class_name, set_class_name, clear_class_name),
1931    /// Only present when different from parent.
1932    (FontFamily, font_family, set_font_family, clear_font_family),
1933    (HtmlTag, html_tag, set_html_tag, clear_html_tag),
1934    /// Inner HTML of an element. Only used for a top-level math element,
1935    /// to support third-party math accessibility products that parse MathML.
1936    (InnerHtml, inner_html, set_inner_html, clear_inner_html),
1937    /// A keystroke or sequence of keystrokes, complete with any required
1938    /// modifiers(s), that will perform this node's default action.
1939    /// The value of this property should be in a human-friendly format.
1940    (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
1941    /// An [IETF language tag](https://www.rfc-editor.org/info/bcp47).
1942    /// Only present when different from parent.
1943    (Language, language, set_language, clear_language),
1944    /// If a text input has placeholder text, it should be exposed
1945    /// through this property rather than [`label`].
1946    ///
1947    /// [`label`]: Node::label
1948    (Placeholder, placeholder, set_placeholder, clear_placeholder),
1949    /// An optional string that may override an assistive technology's
1950    /// description of the node's role. Only provide this for custom control types.
1951    /// The value of this property should be in a human-friendly, localized format.
1952    (RoleDescription, role_description, set_role_description, clear_role_description),
1953    /// An optional string that may override an assistive technology's
1954    /// description of the node's state, replacing default strings such as
1955    /// "checked" or "selected". Note that most platform accessibility APIs
1956    /// and assistive technologies do not support this feature.
1957    (StateDescription, state_description, set_state_description, clear_state_description),
1958    /// If a node's only accessible name comes from a tooltip, it should be
1959    /// exposed through this property rather than [`label`].
1960    ///
1961    /// [`label`]: Node::label
1962    (Tooltip, tooltip, set_tooltip, clear_tooltip),
1963    (Url, url, set_url, clear_url),
1964    (RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
1965    (ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text),
1966    (BrailleLabel, braille_label, set_braille_label, clear_braille_label),
1967    (BrailleRoleDescription, braille_role_description, set_braille_role_description, clear_braille_role_description)
1968}
1969
1970f64_property_methods! {
1971    (ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
1972    (ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
1973    (ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
1974    (ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
1975    (ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
1976    (ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
1977    (NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
1978    (MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
1979    (MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
1980    (NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
1981    (NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump)
1982}
1983
1984f32_property_methods! {
1985    /// Font size is in pixels.
1986    (FontSize, font_size, set_font_size, clear_font_size),
1987    /// Font weight can take on any arbitrary numeric value. Increments of 100 in
1988    /// range `[0, 900]` represent keywords such as light, normal, bold, etc.
1989    (FontWeight, font_weight, set_font_weight, clear_font_weight)
1990}
1991
1992usize_property_methods! {
1993    (RowCount, row_count, set_row_count, clear_row_count),
1994    (ColumnCount, column_count, set_column_count, clear_column_count),
1995    (RowIndex, row_index, set_row_index, clear_row_index),
1996    (ColumnIndex, column_index, set_column_index, clear_column_index),
1997    (RowSpan, row_span, set_row_span, clear_row_span),
1998    (ColumnSpan, column_span, set_column_span, clear_column_span),
1999    (Level, level, set_level, clear_level),
2000    /// For containers like [`Role::ListBox`], specifies the total number of items.
2001    (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
2002    /// For items like [`Role::ListBoxOption`], specifies their index in the item list.
2003    /// This may not exceed the value of [`size_of_set`] as set on the container.
2004    ///
2005    /// [`size_of_set`]: Node::size_of_set
2006    (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
2007}
2008
2009color_property_methods! {
2010    /// For [`Role::ColorWell`], specifies the selected color.
2011    (ColorValue, color_value, set_color_value, clear_color_value),
2012    /// Background color.
2013    (BackgroundColor, background_color, set_background_color, clear_background_color),
2014    /// Foreground color.
2015    (ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
2016}
2017
2018text_decoration_property_methods! {
2019    (Overline, overline, set_overline, clear_overline),
2020    (Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
2021    (Underline, underline, set_underline, clear_underline)
2022}
2023
2024length_slice_property_methods! {
2025    /// For text runs, the length (non-inclusive) of each character
2026    /// in UTF-8 code units (bytes). The sum of these lengths must equal
2027    /// the length of [`value`], also in bytes.
2028    ///
2029    /// A character is defined as the smallest unit of text that
2030    /// can be selected. This isn't necessarily a single Unicode
2031    /// scalar value (code point). This is why AccessKit can't compute
2032    /// the lengths of the characters from the text itself; this information
2033    /// must be provided by the text editing implementation.
2034    ///
2035    /// If this node is the last text run in a line that ends with a hard
2036    /// line break, that line break should be included at the end of this
2037    /// node's value as either a CRLF or LF; in both cases, the line break
2038    /// should be counted as a single character for the sake of this slice.
2039    /// When the caret is at the end of such a line, the focus of the text
2040    /// selection should be on the line break, not after it.
2041    ///
2042    /// [`value`]: Node::value
2043    (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
2044
2045    /// For text runs, the start index of each word in characters, as defined
2046    /// in [`character_lengths`]. This list must be sorted.
2047    ///
2048    /// If this text run doesn't contain the start of any words, but only
2049    /// the middle or end of a word, this list must be empty.
2050    ///
2051    /// If this text run is the first in the document or the first in a paragraph
2052    /// (that is, the previous run ends with a newline character), then the first
2053    /// character of the run is implicitly the start of a word. In this case,
2054    /// beginning this list with `0` is permitted but not necessary.
2055    ///
2056    /// The end of each word is the beginning of the next word; there are no
2057    /// characters that are not considered part of a word. Trailing whitespace
2058    /// is typically considered part of the word that precedes it, while
2059    /// a line's leading whitespace is considered its own word. Whether
2060    /// punctuation is considered a separate word or part of the preceding
2061    /// word depends on the particular text editing implementation.
2062    /// Some editors may have their own definition of a word; for example,
2063    /// in an IDE, words may correspond to programming language tokens.
2064    ///
2065    /// Not all assistive technologies require information about word
2066    /// boundaries, and not all platform accessibility APIs even expose
2067    /// this information, but for assistive technologies that do use
2068    /// this information, users will get unpredictable results if the word
2069    /// boundaries exposed by the accessibility tree don't match
2070    /// the editor's behavior. This is why AccessKit does not determine
2071    /// word boundaries itself.
2072    ///
2073    /// [`character_lengths`]: Node::character_lengths
2074    (WordStarts, word_starts, set_word_starts, clear_word_starts)
2075}
2076
2077coord_slice_property_methods! {
2078    /// For text runs, this is the position of each character within
2079    /// the node's bounding box, in the direction given by
2080    /// [`text_direction`], in the coordinate space of this node.
2081    ///
2082    /// When present, the length of this slice should be the same as the length
2083    /// of [`character_lengths`], including for lines that end
2084    /// with a hard line break. The position of such a line break should
2085    /// be the position where an end-of-paragraph marker would be rendered.
2086    ///
2087    /// This property is optional. Without it, AccessKit can't support some
2088    /// use cases, such as screen magnifiers that track the caret position
2089    /// or screen readers that display a highlight cursor. However,
2090    /// most text functionality still works without this information.
2091    ///
2092    /// [`text_direction`]: Node::text_direction
2093    /// [`character_lengths`]: Node::character_lengths
2094    (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
2095
2096    /// For text runs, this is the advance width of each character,
2097    /// in the direction given by [`text_direction`], in the coordinate
2098    /// space of this node.
2099    ///
2100    /// When present, the length of this slice should be the same as the length
2101    /// of [`character_lengths`], including for lines that end
2102    /// with a hard line break. The width of such a line break should
2103    /// be non-zero if selecting the line break by itself results in
2104    /// a visible highlight (as in Microsoft Word), or zero if not
2105    /// (as in Windows Notepad).
2106    ///
2107    /// This property is optional. Without it, AccessKit can't support some
2108    /// use cases, such as screen magnifiers that track the caret position
2109    /// or screen readers that display a highlight cursor. However,
2110    /// most text functionality still works without this information.
2111    ///
2112    /// [`text_direction`]: Node::text_direction
2113    /// [`character_lengths`]: Node::character_lengths
2114    (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
2115}
2116
2117bool_property_methods! {
2118    /// Whether this node is expanded, collapsed, or neither.
2119    ///
2120    /// Setting this to `false` means the node is collapsed; omitting it means this state
2121    /// isn't applicable.
2122    (Expanded, is_expanded, set_expanded, clear_expanded),
2123
2124    /// Indicates whether this node is selected or unselected.
2125    ///
2126    /// The absence of this flag (as opposed to a `false` setting)
2127    /// means that the concept of "selected" doesn't apply.
2128    /// When deciding whether to set the flag to false or omit it,
2129    /// consider whether it would be appropriate for a screen reader
2130    /// to announce "not selected". The ambiguity of this flag
2131    /// in platform accessibility APIs has made extraneous
2132    /// "not selected" announcements a common annoyance.
2133    (Selected, is_selected, set_selected, clear_selected)
2134}
2135
2136unique_enum_property_methods! {
2137    (Invalid, invalid, set_invalid, clear_invalid, Grammar),
2138    (Toggled, toggled, set_toggled, clear_toggled, True),
2139    (Live, live, set_live, clear_live, Polite),
2140    (TextDirection, text_direction, set_text_direction, clear_text_direction, RightToLeft),
2141    (Orientation, orientation, set_orientation, clear_orientation, Vertical),
2142    (SortDirection, sort_direction, set_sort_direction, clear_sort_direction, Descending),
2143    (AriaCurrent, aria_current, set_aria_current, clear_aria_current, True),
2144    (AutoComplete, auto_complete, set_auto_complete, clear_auto_complete, List),
2145    (HasPopup, has_popup, set_has_popup, clear_has_popup, Menu),
2146    /// The list style type. Only available on list items.
2147    (ListStyle, list_style, set_list_style, clear_list_style, Disc),
2148    (TextAlign, text_align, set_text_align, clear_text_align, Right),
2149    (VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset, Superscript)
2150}
2151
2152property_methods! {
2153    /// An affine transform to apply to any coordinates within this node
2154    /// and its descendants, including the [`bounds`] property of this node.
2155    /// The combined transforms of this node and its ancestors define
2156    /// the coordinate space of this node. /// This should be `None` if
2157    /// it would be set to the identity transform, which should be the case
2158    /// for most nodes.
2159    ///
2160    /// AccessKit expects the final transformed coordinates to be relative
2161    /// to the origin of the tree's container (e.g. window), in physical
2162    /// pixels, with the y coordinate being top-down.
2163    ///
2164    /// [`bounds`]: Node::bounds
2165    (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
2166
2167    /// The bounding box of this node, in the node's coordinate space.
2168    /// This property does not affect the coordinate space of either this node
2169    /// or its descendants; only the [`transform`] property affects that.
2170    /// This, along with the recommendation that most nodes should have
2171    /// a [`transform`] of `None`, implies that the `bounds` property
2172    /// of most nodes should be in the coordinate space of the nearest ancestor
2173    /// with a non-`None` [`transform`], or if there is no such ancestor,
2174    /// the tree's container (e.g. window).
2175    ///
2176    /// [`transform`]: Node::transform
2177    (Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
2178
2179    (TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection),
2180
2181    /// The tree that this node grafts. When set, this node acts as a graft
2182    /// point, and its child is the root of the specified subtree.
2183    ///
2184    /// A graft node must be created before its subtree is pushed.
2185    ///
2186    /// Removing a graft node or clearing this property removes its subtree,
2187    /// unless a new graft node is provided in the same update.
2188    (TreeId, tree_id, get_tree_id_property, Option<TreeId>, set_tree_id, set_tree_id_property, TreeId, clear_tree_id)
2189}
2190
2191impl Node {
2192    option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection, tree_id,] }
2193}
2194
2195#[cfg(test)]
2196mod transform {
2197    use super::{Affine, Node, Role};
2198
2199    #[test]
2200    fn getter_should_return_default_value() {
2201        let node = Node::new(Role::Unknown);
2202        assert!(node.transform().is_none());
2203    }
2204    #[test]
2205    fn setter_should_update_the_property() {
2206        let mut node = Node::new(Role::Unknown);
2207        node.set_transform(Affine::IDENTITY);
2208        assert_eq!(node.transform(), Some(&Affine::IDENTITY));
2209    }
2210    #[test]
2211    fn clearer_should_reset_the_property() {
2212        let mut node = Node::new(Role::Unknown);
2213        node.set_transform(Affine::IDENTITY);
2214        node.clear_transform();
2215        assert!(node.transform().is_none());
2216    }
2217}
2218
2219#[cfg(test)]
2220mod bounds {
2221    use super::{Node, Rect, Role};
2222
2223    #[test]
2224    fn getter_should_return_default_value() {
2225        let node = Node::new(Role::Unknown);
2226        assert!(node.bounds().is_none());
2227    }
2228    #[test]
2229    fn setter_should_update_the_property() {
2230        let mut node = Node::new(Role::Unknown);
2231        let value = Rect {
2232            x0: 0.0,
2233            y0: 1.0,
2234            x1: 2.0,
2235            y1: 3.0,
2236        };
2237        node.set_bounds(value);
2238        assert_eq!(node.bounds(), Some(value));
2239    }
2240    #[test]
2241    fn clearer_should_reset_the_property() {
2242        let mut node = Node::new(Role::Unknown);
2243        node.set_bounds(Rect {
2244            x0: 0.0,
2245            y0: 1.0,
2246            x1: 2.0,
2247            y1: 3.0,
2248        });
2249        node.clear_bounds();
2250        assert!(node.bounds().is_none());
2251    }
2252}
2253
2254#[cfg(test)]
2255mod text_selection {
2256    use super::{Node, NodeId, Role, TextPosition, TextSelection};
2257
2258    #[test]
2259    fn getter_should_return_default_value() {
2260        let node = Node::new(Role::Unknown);
2261        assert!(node.text_selection().is_none());
2262    }
2263    #[test]
2264    fn setter_should_update_the_property() {
2265        let mut node = Node::new(Role::Unknown);
2266        let value = TextSelection {
2267            anchor: TextPosition {
2268                node: NodeId(0),
2269                character_index: 0,
2270            },
2271            focus: TextPosition {
2272                node: NodeId(0),
2273                character_index: 2,
2274            },
2275        };
2276        node.set_text_selection(value);
2277        assert_eq!(node.text_selection(), Some(&value));
2278    }
2279    #[test]
2280    fn clearer_should_reset_the_property() {
2281        let mut node = Node::new(Role::Unknown);
2282        node.set_text_selection(TextSelection {
2283            anchor: TextPosition {
2284                node: NodeId(0),
2285                character_index: 0,
2286            },
2287            focus: TextPosition {
2288                node: NodeId(0),
2289                character_index: 2,
2290            },
2291        });
2292        node.clear_text_selection();
2293        assert!(node.text_selection().is_none());
2294    }
2295}
2296
2297#[cfg(test)]
2298mod tree_id {
2299    use super::{Node, Role, TreeId, Uuid};
2300
2301    #[test]
2302    fn getter_should_return_default_value() {
2303        let node = Node::new(Role::GenericContainer);
2304        assert!(node.tree_id().is_none());
2305    }
2306    #[test]
2307    fn setter_should_update_the_property() {
2308        let mut node = Node::new(Role::GenericContainer);
2309        let value = TreeId(Uuid::nil());
2310        node.set_tree_id(value);
2311        assert_eq!(node.tree_id(), Some(value));
2312    }
2313    #[test]
2314    fn clearer_should_reset_the_property() {
2315        let mut node = Node::new(Role::GenericContainer);
2316        node.set_tree_id(TreeId(Uuid::nil()));
2317        node.clear_tree_id();
2318        assert!(node.tree_id().is_none());
2319    }
2320}
2321
2322vec_property_methods! {
2323    (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)
2324}
2325
2326#[cfg(test)]
2327mod custom_actions {
2328    use super::{CustomAction, Node, Role};
2329    use core::slice;
2330
2331    #[test]
2332    fn getter_should_return_default_value() {
2333        let node = Node::new(Role::Unknown);
2334        assert!(node.custom_actions().is_empty());
2335    }
2336    #[test]
2337    fn setter_should_update_the_property() {
2338        let mut node = Node::new(Role::Unknown);
2339        let value = alloc::vec![
2340            CustomAction {
2341                id: 0,
2342                description: "first test action".into(),
2343            },
2344            CustomAction {
2345                id: 1,
2346                description: "second test action".into(),
2347            },
2348        ];
2349        node.set_custom_actions(value.clone());
2350        assert_eq!(node.custom_actions(), value);
2351    }
2352    #[test]
2353    fn pusher_should_update_the_property() {
2354        let mut node = Node::new(Role::Unknown);
2355        let first_action = CustomAction {
2356            id: 0,
2357            description: "first test action".into(),
2358        };
2359        let second_action = CustomAction {
2360            id: 1,
2361            description: "second test action".into(),
2362        };
2363        node.push_custom_action(first_action.clone());
2364        assert_eq!(node.custom_actions(), slice::from_ref(&first_action));
2365        node.push_custom_action(second_action.clone());
2366        assert_eq!(node.custom_actions(), &[first_action, second_action]);
2367    }
2368    #[test]
2369    fn clearer_should_reset_the_property() {
2370        let mut node = Node::new(Role::Unknown);
2371        node.set_custom_actions([CustomAction {
2372            id: 0,
2373            description: "test action".into(),
2374        }]);
2375        node.clear_custom_actions();
2376        assert!(node.custom_actions().is_empty());
2377    }
2378}
2379
2380impl fmt::Debug for Node {
2381    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2382        let mut fmt = f.debug_struct("Node");
2383
2384        fmt.field("role", &self.role());
2385
2386        let supported_actions = action_mask_to_action_vec(self.actions);
2387        if !supported_actions.is_empty() {
2388            fmt.field("actions", &supported_actions);
2389        }
2390
2391        let child_supported_actions = action_mask_to_action_vec(self.child_actions);
2392        if !child_supported_actions.is_empty() {
2393            fmt.field("child_actions", &child_supported_actions);
2394        }
2395
2396        self.debug_flag_properties(&mut fmt);
2397        self.debug_node_id_vec_properties(&mut fmt);
2398        self.debug_node_id_properties(&mut fmt);
2399        self.debug_string_properties(&mut fmt);
2400        self.debug_f64_properties(&mut fmt);
2401        self.debug_f32_properties(&mut fmt);
2402        self.debug_usize_properties(&mut fmt);
2403        self.debug_color_properties(&mut fmt);
2404        self.debug_text_decoration_properties(&mut fmt);
2405        self.debug_length_slice_properties(&mut fmt);
2406        self.debug_coord_slice_properties(&mut fmt);
2407        self.debug_bool_properties(&mut fmt);
2408        self.debug_unique_enum_properties(&mut fmt);
2409        self.debug_option_properties(&mut fmt);
2410
2411        let custom_actions = self.custom_actions();
2412        if !custom_actions.is_empty() {
2413            fmt.field("custom_actions", &custom_actions);
2414        }
2415
2416        fmt.finish()
2417    }
2418}
2419
2420#[cfg(feature = "serde")]
2421macro_rules! serialize_property {
2422    ($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
2423        match &$self.values[$index as usize] {
2424            PropertyValue::None => (),
2425            $(PropertyValue::$variant(value) => {
2426                $map.serialize_entry(&$id, &value)?;
2427            })*
2428        }
2429    }
2430}
2431
2432#[cfg(feature = "serde")]
2433macro_rules! deserialize_property {
2434    ($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
2435        match $key {
2436            $($(PropertyId::$id => {
2437                let value = $map.next_value()?;
2438                $props.set(PropertyId::$id, PropertyValue::$type(value));
2439            })*)*
2440            PropertyId::Unset => {
2441                let _ = $map.next_value::<IgnoredAny>()?;
2442            }
2443        }
2444    }
2445}
2446
2447#[cfg(feature = "serde")]
2448impl Serialize for Properties {
2449    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2450    where
2451        S: Serializer,
2452    {
2453        let mut len = 0;
2454        for value in &*self.values {
2455            if !matches!(*value, PropertyValue::None) {
2456                len += 1;
2457            }
2458        }
2459        let mut map = serializer.serialize_map(Some(len))?;
2460        for (id, index) in self.indices.0.iter().copied().enumerate() {
2461            if index == PropertyId::Unset as u8 {
2462                continue;
2463            }
2464            let id = PropertyId::n(id as _).unwrap();
2465            serialize_property!(self, map, index, id, {
2466                NodeIdVec,
2467                NodeId,
2468                String,
2469                F64,
2470                F32,
2471                Usize,
2472                Color,
2473                TextDecoration,
2474                LengthSlice,
2475                CoordSlice,
2476                Bool,
2477                Invalid,
2478                Toggled,
2479                Live,
2480                TextDirection,
2481                Orientation,
2482                SortDirection,
2483                AriaCurrent,
2484                AutoComplete,
2485                HasPopup,
2486                ListStyle,
2487                TextAlign,
2488                VerticalOffset,
2489                Affine,
2490                Rect,
2491                TextSelection,
2492                CustomActionVec,
2493                TreeId
2494            });
2495        }
2496        map.end()
2497    }
2498}
2499
2500#[cfg(feature = "serde")]
2501struct PropertiesVisitor;
2502
2503#[cfg(feature = "serde")]
2504impl<'de> Visitor<'de> for PropertiesVisitor {
2505    type Value = Properties;
2506
2507    #[inline]
2508    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2509        formatter.write_str("property map")
2510    }
2511
2512    fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
2513    where
2514        V: MapAccess<'de>,
2515    {
2516        let mut props = Properties::default();
2517        while let Some(id) = map.next_key()? {
2518            deserialize_property!(props, map, id, {
2519                NodeIdVec {
2520                    Children,
2521                    Controls,
2522                    Details,
2523                    DescribedBy,
2524                    FlowTo,
2525                    LabelledBy,
2526                    Owns,
2527                    RadioGroup
2528                },
2529                NodeId {
2530                    ActiveDescendant,
2531                    ErrorMessage,
2532                    InPageLinkTarget,
2533                    MemberOf,
2534                    NextOnLine,
2535                    PreviousOnLine,
2536                    PopupFor
2537                },
2538                String {
2539                    Label,
2540                    Description,
2541                    Value,
2542                    AccessKey,
2543                    AuthorId,
2544                    ClassName,
2545                    FontFamily,
2546                    HtmlTag,
2547                    InnerHtml,
2548                    KeyboardShortcut,
2549                    Language,
2550                    Placeholder,
2551                    RoleDescription,
2552                    StateDescription,
2553                    Tooltip,
2554                    Url,
2555                    RowIndexText,
2556                    ColumnIndexText,
2557                    BrailleLabel,
2558                    BrailleRoleDescription
2559                },
2560                F64 {
2561                    ScrollX,
2562                    ScrollXMin,
2563                    ScrollXMax,
2564                    ScrollY,
2565                    ScrollYMin,
2566                    ScrollYMax,
2567                    NumericValue,
2568                    MinNumericValue,
2569                    MaxNumericValue,
2570                    NumericValueStep,
2571                    NumericValueJump
2572                },
2573                F32 {
2574                    FontSize,
2575                    FontWeight
2576                },
2577                Usize {
2578                    RowCount,
2579                    ColumnCount,
2580                    RowIndex,
2581                    ColumnIndex,
2582                    RowSpan,
2583                    ColumnSpan,
2584                    Level,
2585                    SizeOfSet,
2586                    PositionInSet
2587                },
2588                Color {
2589                    ColorValue,
2590                    BackgroundColor,
2591                    ForegroundColor
2592                },
2593                TextDecoration {
2594                    Overline,
2595                    Strikethrough,
2596                    Underline
2597                },
2598                LengthSlice {
2599                    CharacterLengths,
2600                    WordStarts
2601                },
2602                CoordSlice {
2603                    CharacterPositions,
2604                    CharacterWidths
2605                },
2606                Bool {
2607                    Expanded,
2608                    Selected
2609                },
2610                Invalid { Invalid },
2611                Toggled { Toggled },
2612                Live { Live },
2613                TextDirection { TextDirection },
2614                Orientation { Orientation },
2615                SortDirection { SortDirection },
2616                AriaCurrent { AriaCurrent },
2617                AutoComplete { AutoComplete },
2618                HasPopup { HasPopup },
2619                ListStyle { ListStyle },
2620                TextAlign { TextAlign },
2621                VerticalOffset { VerticalOffset },
2622                Affine { Transform },
2623                Rect { Bounds },
2624                TextSelection { TextSelection },
2625                CustomActionVec { CustomActions },
2626                TreeId { TreeId }
2627            });
2628        }
2629
2630        Ok(props)
2631    }
2632}
2633
2634#[cfg(feature = "serde")]
2635impl<'de> Deserialize<'de> for Properties {
2636    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2637    where
2638        D: Deserializer<'de>,
2639    {
2640        deserializer.deserialize_map(PropertiesVisitor)
2641    }
2642}
2643
2644#[cfg(feature = "schemars")]
2645macro_rules! add_schema_property {
2646    ($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
2647        let name = format!("{:?}", $enum_value);
2648        let name = name[..1].to_ascii_lowercase() + &name[1..];
2649        let subschema = $gen.subschema_for::<$type>();
2650        $properties.insert(name, SchemaValue::from(subschema));
2651    }};
2652}
2653
2654#[cfg(feature = "schemars")]
2655macro_rules! add_properties_to_schema {
2656    ($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
2657        $($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
2658    }
2659}
2660
2661#[cfg(feature = "schemars")]
2662impl JsonSchema for Properties {
2663    #[inline]
2664    fn schema_name() -> Cow<'static, str> {
2665        "Properties".into()
2666    }
2667
2668    fn json_schema(generator: &mut SchemaGenerator) -> Schema {
2669        let mut properties = SchemaMap::<String, SchemaValue>::new();
2670        add_properties_to_schema!(generator, properties, {
2671            Vec<NodeId> {
2672                Children,
2673                Controls,
2674                Details,
2675                DescribedBy,
2676                FlowTo,
2677                LabelledBy,
2678                Owns,
2679                RadioGroup
2680            },
2681            NodeId {
2682                ActiveDescendant,
2683                ErrorMessage,
2684                InPageLinkTarget,
2685                MemberOf,
2686                NextOnLine,
2687                PreviousOnLine,
2688                PopupFor
2689            },
2690            Box<str> {
2691                Label,
2692                Description,
2693                Value,
2694                AccessKey,
2695                AuthorId,
2696                ClassName,
2697                FontFamily,
2698                HtmlTag,
2699                InnerHtml,
2700                KeyboardShortcut,
2701                Language,
2702                Placeholder,
2703                RoleDescription,
2704                StateDescription,
2705                Tooltip,
2706                Url,
2707                RowIndexText,
2708                ColumnIndexText,
2709                BrailleLabel,
2710                BrailleRoleDescription
2711            },
2712            f64 {
2713                ScrollX,
2714                ScrollXMin,
2715                ScrollXMax,
2716                ScrollY,
2717                ScrollYMin,
2718                ScrollYMax,
2719                NumericValue,
2720                MinNumericValue,
2721                MaxNumericValue,
2722                NumericValueStep,
2723                NumericValueJump
2724            },
2725            f32 {
2726                FontSize,
2727                FontWeight
2728            },
2729            usize {
2730                RowCount,
2731                ColumnCount,
2732                RowIndex,
2733                ColumnIndex,
2734                RowSpan,
2735                ColumnSpan,
2736                Level,
2737                SizeOfSet,
2738                PositionInSet
2739            },
2740            Color {
2741                ColorValue,
2742                BackgroundColor,
2743                ForegroundColor
2744            },
2745            TextDecoration {
2746                Overline,
2747                Strikethrough,
2748                Underline
2749            },
2750            Box<[u8]> {
2751                CharacterLengths,
2752                WordStarts
2753            },
2754            Box<[f32]> {
2755                CharacterPositions,
2756                CharacterWidths
2757            },
2758            bool {
2759                Expanded,
2760                Selected
2761            },
2762            Invalid { Invalid },
2763            Toggled { Toggled },
2764            Live { Live },
2765            TextDirection { TextDirection },
2766            Orientation { Orientation },
2767            SortDirection { SortDirection },
2768            AriaCurrent { AriaCurrent },
2769            AutoComplete { AutoComplete },
2770            HasPopup { HasPopup },
2771            ListStyle { ListStyle },
2772            TextAlign { TextAlign },
2773            VerticalOffset { VerticalOffset },
2774            Affine { Transform },
2775            Rect { Bounds },
2776            TextSelection { TextSelection },
2777            Vec<CustomAction> { CustomActions }
2778        });
2779        json_schema!({
2780            "type": "object",
2781            "properties": properties
2782        })
2783    }
2784}
2785
2786/// The data associated with an accessibility tree that's global to the
2787/// tree and not associated with any particular node.
2788#[derive(Clone, Debug, PartialEq, Eq)]
2789#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2790#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2791#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2792#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2793pub struct Tree {
2794    /// The identifier of the tree's root node.
2795    pub root: NodeId,
2796    /// The name of the UI toolkit in use.
2797    pub toolkit_name: Option<String>,
2798    /// The version of the UI toolkit.
2799    pub toolkit_version: Option<String>,
2800}
2801
2802impl Tree {
2803    #[inline]
2804    pub fn new(root: NodeId) -> Tree {
2805        Tree {
2806            root,
2807            toolkit_name: None,
2808            toolkit_version: None,
2809        }
2810    }
2811}
2812
2813/// A serializable representation of an atomic change to a [`Tree`].
2814///
2815/// The sender and receiver must be in sync; the update is only meant
2816/// to bring the tree from a specific previous state into its next state.
2817/// Trying to apply it to the wrong tree should immediately panic.
2818///
2819/// Note that for performance, an update should only include nodes that are
2820/// new or changed. AccessKit platform adapters will avoid raising extraneous
2821/// events for nodes that have not changed since the previous update,
2822/// but there is still a cost in processing these nodes and replacing
2823/// the previous instances.
2824#[derive(Clone, Debug, PartialEq)]
2825#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2826#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2827#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2828#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2829pub struct TreeUpdate {
2830    /// Zero or more new or updated nodes. Order doesn't matter.
2831    ///
2832    /// Each node in this list will overwrite any existing node with the same ID.
2833    /// This means that when updating a node, fields that are unchanged
2834    /// from the previous version must still be set to the same values
2835    /// as before.
2836    ///
2837    /// It is an error for any node in this list to not be either the root
2838    /// or a child of another node. For nodes other than the root, the parent
2839    /// must be either an unchanged node already in the tree, or another node
2840    /// in this list.
2841    ///
2842    /// To add a child to the tree, the list must include both the child
2843    /// and an updated version of the parent with the child's ID added to
2844    /// [`Node::children`].
2845    ///
2846    /// To remove a child and all of its descendants, this list must include
2847    /// an updated version of the parent node with the child's ID removed
2848    /// from [`Node::children`]. Neither the child nor any of its descendants
2849    /// may be included in this list.
2850    pub nodes: Vec<(NodeId, Node)>,
2851
2852    /// Rarely updated information about the tree as a whole. This may be omitted
2853    /// if it has not changed since the previous update, but providing the same
2854    /// information again is also allowed. This is required when initializing
2855    /// a tree.
2856    pub tree: Option<Tree>,
2857
2858    /// The identifier of the tree that this update applies to.
2859    ///
2860    /// Use [`TreeId::ROOT`] for the main/root tree. For subtrees, use a unique
2861    /// [`TreeId`] that identifies the subtree.
2862    ///
2863    /// When updating a subtree (non-ROOT tree_id):
2864    /// - A graft node with [`Node::tree_id`] set to this tree's ID must already
2865    ///   exist in the parent tree before the first subtree update.
2866    /// - The first update for a subtree must include [`tree`](Self::tree) data.
2867    pub tree_id: TreeId,
2868
2869    /// The node within this tree that has keyboard focus when the native
2870    /// host (e.g. window) has focus. If no specific node within the tree
2871    /// has keyboard focus, this must be set to the root. The latest focus state
2872    /// must be provided with every tree update, even if the focus state
2873    /// didn't change in a given update.
2874    ///
2875    /// For subtrees, this specifies which node has focus when the subtree
2876    /// itself is focused (i.e., when focus is on the graft node in the parent
2877    /// tree).
2878    pub focus: NodeId,
2879}
2880
2881/// The amount by which to scroll in the direction specified by one of the
2882/// `Scroll` actions.
2883#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2884#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2885#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2886#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2887#[cfg_attr(
2888    feature = "pyo3",
2889    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
2890)]
2891#[repr(u8)]
2892pub enum ScrollUnit {
2893    /// A single item of a list, line of text (for vertical scrolling),
2894    /// character (for horizontal scrolling), or an approximation of
2895    /// one of these.
2896    Item,
2897    /// The amount of content that fits in the viewport.
2898    Page,
2899}
2900
2901/// A suggestion about where the node being scrolled into view should be
2902/// positioned relative to the edges of the scrollable container.
2903#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2904#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2905#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2906#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2907#[cfg_attr(
2908    feature = "pyo3",
2909    pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
2910)]
2911#[repr(u8)]
2912pub enum ScrollHint {
2913    TopLeft,
2914    BottomRight,
2915    TopEdge,
2916    BottomEdge,
2917    LeftEdge,
2918    RightEdge,
2919}
2920
2921#[derive(Clone, Debug, PartialEq)]
2922#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2923#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2924#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2925#[repr(C)]
2926pub enum ActionData {
2927    CustomAction(i32),
2928    Value(Box<str>),
2929    NumericValue(f64),
2930    ScrollUnit(ScrollUnit),
2931    /// Optional suggestion for [`Action::ScrollIntoView`], specifying
2932    /// the preferred position of the target node relative to the scrollable
2933    /// container's viewport.
2934    ScrollHint(ScrollHint),
2935    /// Target for [`Action::ScrollToPoint`], in platform-native coordinates
2936    /// relative to the origin of the tree's container (e.g. window).
2937    ScrollToPoint(Point),
2938    /// Target for [`Action::SetScrollOffset`], in the coordinate space
2939    /// of the action's target node.
2940    SetScrollOffset(Point),
2941    SetTextSelection(TextSelection),
2942}
2943
2944#[derive(Clone, Debug, PartialEq)]
2945#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2946#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2947#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2948#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2949pub struct ActionRequest {
2950    pub action: Action,
2951    pub target_tree: TreeId,
2952    pub target_node: NodeId,
2953    pub data: Option<ActionData>,
2954}
2955
2956/// Handles activation of the application's accessibility implementation.
2957pub trait ActivationHandler {
2958    /// Requests a [`TreeUpdate`] with a full tree. If the application
2959    /// can generate the tree synchronously within this method call,
2960    /// it should do so and return the [`TreeUpdate`]. Otherwise,
2961    /// it must send the update to the platform adapter asynchronously,
2962    /// no later than the next display refresh, even if a frame would not
2963    /// normally be rendered due to user input or other activity.
2964    /// The application should not return or send a placeholder [`TreeUpdate`];
2965    /// the platform adapter will provide one if necessary until the real
2966    /// tree is sent.
2967    ///
2968    /// The primary purpose of this method is to allow the application
2969    /// to lazily initialize its accessibility implementation. However,
2970    /// this method may be called consecutively without any call to
2971    /// [`DeactivationHandler::deactivate_accessibility`]; this typically happens
2972    /// if the platform adapter merely forwards tree updates to assistive
2973    /// technologies without maintaining any state. A call to this method
2974    /// must always generate a [`TreeUpdate`] with a full tree, even if
2975    /// the application normally sends incremental updates.
2976    ///
2977    /// The thread on which this method is called is platform-dependent.
2978    /// Refer to the platform adapter documentation for more details.
2979    fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
2980}
2981
2982/// Handles requests from assistive technologies or other clients.
2983pub trait ActionHandler {
2984    /// Perform the requested action. If the requested action is not supported,
2985    /// this method must do nothing.
2986    ///
2987    /// The thread on which this method is called is platform-dependent.
2988    /// Refer to the platform adapter documentation for more details.
2989    ///
2990    /// This method may queue the request and handle it asynchronously.
2991    /// This behavior is preferred over blocking, e.g. when dispatching
2992    /// the request to another thread.
2993    fn do_action(&mut self, request: ActionRequest);
2994}
2995
2996/// Handles deactivation of the application's accessibility implementation.
2997pub trait DeactivationHandler {
2998    /// Deactivate the application's accessibility implementation and drop any
2999    /// associated data that can be reconstructed later. After this method
3000    /// is called, if an accessibility tree is needed again, the platform
3001    /// adapter will call [`ActivationHandler::request_initial_tree`] again.
3002    ///
3003    /// The thread on which this method is called is platform-dependent.
3004    /// Refer to the platform adapter documentation for more details.
3005    fn deactivate_accessibility(&mut self);
3006}
3007
3008#[cfg(test)]
3009mod tests {
3010    use super::*;
3011    use alloc::format;
3012
3013    #[test]
3014    fn u64_should_be_convertible_to_node_id() {
3015        assert_eq!(NodeId::from(0u64), NodeId(0));
3016        assert_eq!(NodeId::from(1u64), NodeId(1));
3017    }
3018
3019    #[test]
3020    fn node_id_should_be_convertible_to_u64() {
3021        assert_eq!(u64::from(NodeId(0)), 0u64);
3022        assert_eq!(u64::from(NodeId(1)), 1u64);
3023    }
3024
3025    #[test]
3026    fn node_id_should_have_debug_repr() {
3027        assert_eq!(&format!("{:?}", NodeId(0)), "#0");
3028        assert_eq!(&format!("{:?}", NodeId(1)), "#1");
3029    }
3030
3031    #[test]
3032    fn action_n_should_return_the_corresponding_variant() {
3033        assert_eq!(Action::n(0), Some(Action::Click));
3034        assert_eq!(Action::n(1), Some(Action::Focus));
3035        assert_eq!(Action::n(2), Some(Action::Blur));
3036        assert_eq!(Action::n(3), Some(Action::Collapse));
3037        assert_eq!(Action::n(4), Some(Action::Expand));
3038        assert_eq!(Action::n(5), Some(Action::CustomAction));
3039        assert_eq!(Action::n(6), Some(Action::Decrement));
3040        assert_eq!(Action::n(7), Some(Action::Increment));
3041        assert_eq!(Action::n(8), Some(Action::HideTooltip));
3042        assert_eq!(Action::n(9), Some(Action::ShowTooltip));
3043        assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
3044        assert_eq!(Action::n(11), Some(Action::ScrollDown));
3045        assert_eq!(Action::n(12), Some(Action::ScrollLeft));
3046        assert_eq!(Action::n(13), Some(Action::ScrollRight));
3047        assert_eq!(Action::n(14), Some(Action::ScrollUp));
3048        assert_eq!(Action::n(15), Some(Action::ScrollIntoView));
3049        assert_eq!(Action::n(16), Some(Action::ScrollToPoint));
3050        assert_eq!(Action::n(17), Some(Action::SetScrollOffset));
3051        assert_eq!(Action::n(18), Some(Action::SetTextSelection));
3052        assert_eq!(
3053            Action::n(19),
3054            Some(Action::SetSequentialFocusNavigationStartingPoint)
3055        );
3056        assert_eq!(Action::n(20), Some(Action::SetValue));
3057        assert_eq!(Action::n(21), Some(Action::ShowContextMenu));
3058        assert_eq!(Action::n(22), None);
3059    }
3060
3061    #[test]
3062    fn empty_action_mask_should_be_converted_to_empty_vec() {
3063        assert_eq!(
3064            Vec::<Action>::new(),
3065            action_mask_to_action_vec(Node::new(Role::Unknown).actions)
3066        );
3067    }
3068
3069    #[test]
3070    fn action_mask_should_be_convertible_to_vec() {
3071        let mut node = Node::new(Role::Unknown);
3072        node.add_action(Action::Click);
3073        assert_eq!(
3074            &[Action::Click],
3075            action_mask_to_action_vec(node.actions).as_slice()
3076        );
3077
3078        let mut node = Node::new(Role::Unknown);
3079        node.add_action(Action::ShowContextMenu);
3080        assert_eq!(
3081            &[Action::ShowContextMenu],
3082            action_mask_to_action_vec(node.actions).as_slice()
3083        );
3084
3085        let mut node = Node::new(Role::Unknown);
3086        node.add_action(Action::Click);
3087        node.add_action(Action::ShowContextMenu);
3088        assert_eq!(
3089            &[Action::Click, Action::ShowContextMenu],
3090            action_mask_to_action_vec(node.actions).as_slice()
3091        );
3092
3093        let mut node = Node::new(Role::Unknown);
3094        node.add_action(Action::Focus);
3095        node.add_action(Action::Blur);
3096        node.add_action(Action::Collapse);
3097        assert_eq!(
3098            &[Action::Focus, Action::Blur, Action::Collapse],
3099            action_mask_to_action_vec(node.actions).as_slice()
3100        );
3101    }
3102
3103    #[test]
3104    fn new_node_should_have_user_provided_role() {
3105        let node = Node::new(Role::Button);
3106        assert_eq!(node.role(), Role::Button);
3107    }
3108
3109    #[test]
3110    fn node_role_setter_should_update_the_role() {
3111        let mut node = Node::new(Role::Button);
3112        node.set_role(Role::CheckBox);
3113        assert_eq!(node.role(), Role::CheckBox);
3114    }
3115
3116    macro_rules! assert_absent_action {
3117        ($node:ident, $action:ident) => {
3118            assert!(!$node.supports_action(Action::$action));
3119            assert!(!$node.child_supports_action(Action::$action));
3120        };
3121    }
3122
3123    #[test]
3124    fn new_node_should_not_support_anyaction() {
3125        let node = Node::new(Role::Unknown);
3126        assert_absent_action!(node, Click);
3127        assert_absent_action!(node, Focus);
3128        assert_absent_action!(node, Blur);
3129        assert_absent_action!(node, Collapse);
3130        assert_absent_action!(node, Expand);
3131        assert_absent_action!(node, CustomAction);
3132        assert_absent_action!(node, Decrement);
3133        assert_absent_action!(node, Increment);
3134        assert_absent_action!(node, HideTooltip);
3135        assert_absent_action!(node, ShowTooltip);
3136        assert_absent_action!(node, ReplaceSelectedText);
3137        assert_absent_action!(node, ScrollDown);
3138        assert_absent_action!(node, ScrollLeft);
3139        assert_absent_action!(node, ScrollRight);
3140        assert_absent_action!(node, ScrollUp);
3141        assert_absent_action!(node, ScrollIntoView);
3142        assert_absent_action!(node, ScrollToPoint);
3143        assert_absent_action!(node, SetScrollOffset);
3144        assert_absent_action!(node, SetTextSelection);
3145        assert_absent_action!(node, SetSequentialFocusNavigationStartingPoint);
3146        assert_absent_action!(node, SetValue);
3147        assert_absent_action!(node, ShowContextMenu);
3148    }
3149
3150    #[test]
3151    fn node_add_action_should_add_the_action() {
3152        let mut node = Node::new(Role::Unknown);
3153        node.add_action(Action::Focus);
3154        assert!(node.supports_action(Action::Focus));
3155        node.add_action(Action::Blur);
3156        assert!(node.supports_action(Action::Blur));
3157    }
3158
3159    #[test]
3160    fn node_add_child_action_should_add_the_action() {
3161        let mut node = Node::new(Role::Unknown);
3162        node.add_child_action(Action::Focus);
3163        assert!(node.child_supports_action(Action::Focus));
3164        node.add_child_action(Action::Blur);
3165        assert!(node.child_supports_action(Action::Blur));
3166    }
3167
3168    #[test]
3169    fn node_add_action_should_do_nothing_if_the_action_is_already_supported() {
3170        let mut node = Node::new(Role::Unknown);
3171        node.add_action(Action::Focus);
3172        node.add_action(Action::Focus);
3173        assert!(node.supports_action(Action::Focus));
3174    }
3175
3176    #[test]
3177    fn node_add_child_action_should_do_nothing_if_the_action_is_already_supported() {
3178        let mut node = Node::new(Role::Unknown);
3179        node.add_child_action(Action::Focus);
3180        node.add_child_action(Action::Focus);
3181        assert!(node.child_supports_action(Action::Focus));
3182    }
3183
3184    #[test]
3185    fn node_remove_action_should_remove_the_action() {
3186        let mut node = Node::new(Role::Unknown);
3187        node.add_action(Action::Blur);
3188        node.remove_action(Action::Blur);
3189        assert!(!node.supports_action(Action::Blur));
3190    }
3191
3192    #[test]
3193    fn node_remove_child_action_should_remove_the_action() {
3194        let mut node = Node::new(Role::Unknown);
3195        node.add_child_action(Action::Blur);
3196        node.remove_child_action(Action::Blur);
3197        assert!(!node.child_supports_action(Action::Blur));
3198    }
3199
3200    #[test]
3201    fn node_clear_actions_should_remove_all_actions() {
3202        let mut node = Node::new(Role::Unknown);
3203        node.add_action(Action::Focus);
3204        node.add_action(Action::Blur);
3205        node.clear_actions();
3206        assert!(!node.supports_action(Action::Focus));
3207        assert!(!node.supports_action(Action::Blur));
3208    }
3209
3210    #[test]
3211    fn node_clear_child_actions_should_remove_all_actions() {
3212        let mut node = Node::new(Role::Unknown);
3213        node.add_child_action(Action::Focus);
3214        node.add_child_action(Action::Blur);
3215        node.clear_child_actions();
3216        assert!(!node.child_supports_action(Action::Focus));
3217        assert!(!node.child_supports_action(Action::Blur));
3218    }
3219
3220    #[test]
3221    fn node_should_have_debug_repr() {
3222        let mut node = Node::new(Role::Unknown);
3223        node.add_action(Action::Click);
3224        node.add_action(Action::Focus);
3225        node.add_child_action(Action::ScrollIntoView);
3226        node.set_hidden();
3227        node.set_multiselectable();
3228        node.set_children([NodeId(0), NodeId(1)]);
3229        node.set_active_descendant(NodeId(2));
3230        node.push_custom_action(CustomAction {
3231            id: 0,
3232            description: "test action".into(),
3233        });
3234
3235        assert_eq!(
3236            &format!("{node:?}"),
3237            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" }] }"#
3238        );
3239    }
3240
3241    #[test]
3242    fn new_tree_should_have_root_id() {
3243        let tree = Tree::new(NodeId(1));
3244        assert_eq!(tree.root, NodeId(1));
3245        assert_eq!(tree.toolkit_name, None);
3246        assert_eq!(tree.toolkit_version, None);
3247    }
3248}