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