1#![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#[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 ListMarker,
70
71 TreeItem,
72 ListBoxOption,
73 MenuItem,
74 MenuListOption,
75 Paragraph,
76
77 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 GraphicsDocument,
210 GraphicsObject,
211 GraphicsSymbol,
212
213 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 ListGrid,
261
262 Terminal,
266}
267
268#[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 Click,
282
283 Focus,
284 Blur,
285
286 Collapse,
287 Expand,
288
289 CustomAction,
291
292 Decrement,
294 Increment,
296
297 HideTooltip,
298 ShowTooltip,
299
300 ReplaceSelectedText,
304
305 ScrollDown,
307 ScrollLeft,
309 ScrollRight,
311 ScrollUp,
313
314 ScrollIntoView,
317
318 ScrollToPoint,
322
323 SetScrollOffset,
325
326 SetTextSelection,
328
329 SetSequentialFocusNavigationStartingPoint,
333
334 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 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 Horizontal,
405 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#[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 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#[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#[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 pub const ROOT: Self = Self(Uuid::nil());
675}
676
677#[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 pub node: NodeId,
699 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 pub anchor: TextPosition,
715 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#[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#[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#[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 Children,
821 Controls,
822 Details,
823 DescribedBy,
824 FlowTo,
825 LabelledBy,
826 Owns,
827 RadioGroup,
828
829 ActiveDescendant,
831 ErrorMessage,
832 InPageLinkTarget,
833 MemberOf,
834 NextOnLine,
835 PreviousOnLine,
836 PopupFor,
837
838 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 ScrollX,
862 ScrollXMin,
863 ScrollXMax,
864 ScrollY,
865 ScrollYMin,
866 ScrollYMax,
867 NumericValue,
868 MinNumericValue,
869 MaxNumericValue,
870 NumericValueStep,
871 NumericValueJump,
872
873 FontSize,
875 FontWeight,
876
877 RowCount,
879 ColumnCount,
880 RowIndex,
881 ColumnIndex,
882 RowSpan,
883 ColumnSpan,
884 Level,
885 SizeOfSet,
886 PositionInSet,
887
888 ColorValue,
890 BackgroundColor,
891 ForegroundColor,
892
893 Overline,
895 Strikethrough,
896 Underline,
897
898 CharacterLengths,
900 WordStarts,
901
902 CharacterPositions,
904 CharacterWidths,
905
906 Expanded,
908 Selected,
909
910 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 Transform,
926 Bounds,
927 TextSelection,
928 CustomActions,
929 TreeId,
930
931 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#[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 #[inline]
1700 pub fn child_supports_action(&self, action: Action) -> bool {
1701 (self.child_actions & action.mask()) != 0
1702 }
1703 #[inline]
1706 pub fn add_child_action(&mut self, action: Action) {
1707 self.child_actions |= action.mask();
1708 }
1709 #[inline]
1712 pub fn remove_child_action(&mut self, action: Action) {
1713 self.child_actions &= !(action.mask());
1714 }
1715 #[inline]
1718 pub fn clear_child_actions(&mut self) {
1719 self.child_actions = 0;
1720 }
1721}
1722
1723flag_methods! {
1724 (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 (Modal, is_modal, set_modal, clear_modal),
1734 (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1738 (ReadOnly, is_read_only, set_read_only, clear_read_only),
1740 (Disabled, is_disabled, set_disabled, clear_disabled),
1742 (Italic, is_italic, set_italic, clear_italic),
1743 (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1746 (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1749 (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 (Owns, owns, set_owns, push_owned, clear_owns),
1818 (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
1821}
1822
1823node_id_property_methods! {
1824 (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 (Label, label, set_label, clear_label),
1842 (Description, description, set_description, clear_description),
1843 (Value, value, set_value, clear_value),
1844 (AccessKey, access_key, set_access_key, clear_access_key),
1852 (AuthorId, author_id, set_author_id, clear_author_id),
1855 (ClassName, class_name, set_class_name, clear_class_name),
1856 (FontFamily, font_family, set_font_family, clear_font_family),
1858 (HtmlTag, html_tag, set_html_tag, clear_html_tag),
1859 (InnerHtml, inner_html, set_inner_html, clear_inner_html),
1862 (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
1866 (Language, language, set_language, clear_language),
1869 (Placeholder, placeholder, set_placeholder, clear_placeholder),
1874 (RoleDescription, role_description, set_role_description, clear_role_description),
1878 (StateDescription, state_description, set_state_description, clear_state_description),
1883 (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 (FontSize, font_size, set_font_size, clear_font_size),
1912 (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 (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
1927 (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
1932}
1933
1934color_property_methods! {
1935 (ColorValue, color_value, set_color_value, clear_color_value),
1937 (BackgroundColor, background_color, set_background_color, clear_background_color),
1939 (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 (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
1969
1970 (WordStarts, word_starts, set_word_starts, clear_word_starts)
2000}
2001
2002coord_slice_property_methods! {
2003 (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
2020
2021 (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
2040}
2041
2042bool_property_methods! {
2043 (Expanded, is_expanded, set_expanded, clear_expanded),
2048
2049 (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 (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 (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
2091
2092 (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 (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#[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 pub root: NodeId,
2721 pub toolkit_name: Option<String>,
2723 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#[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 pub nodes: Vec<(NodeId, Node)>,
2776
2777 pub tree: Option<Tree>,
2782
2783 pub tree_id: TreeId,
2793
2794 pub focus: NodeId,
2804}
2805
2806#[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 Item,
2822 Page,
2824}
2825
2826#[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 ScrollHint(ScrollHint),
2860 ScrollToPoint(Point),
2863 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
2881pub trait ActivationHandler {
2883 fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
2905}
2906
2907pub trait ActionHandler {
2909 fn do_action(&mut self, request: ActionRequest);
2919}
2920
2921pub trait DeactivationHandler {
2923 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}