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::{JsonSchema, Schema, SchemaGenerator, json_schema};
23#[cfg(feature = "serde")]
24use serde::{
25 Deserialize, Serialize,
26 de::{Deserializer, IgnoredAny, MapAccess, Visitor},
27 ser::{SerializeMap, Serializer},
28};
29#[cfg(feature = "schemars")]
30use serde_json::{Map as SchemaMap, Value as SchemaValue};
31
32pub use uuid::Uuid;
33
34mod geometry;
35pub use geometry::{Affine, Point, Rect, Size, Vec2};
36
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(feature = "enumn", derive(enumn::N))]
49#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
50#[cfg_attr(feature = "schemars", derive(JsonSchema))]
51#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
52#[cfg_attr(
53 feature = "pyo3",
54 pyclass(
55 module = "accesskit",
56 rename_all = "SCREAMING_SNAKE_CASE",
57 eq,
58 from_py_object
59 )
60)]
61#[repr(u8)]
62pub enum Role {
63 #[default]
64 Unknown,
65 TextRun,
66 Cell,
67 Label,
68 Image,
69 Link,
70 Row,
71 ListItem,
72
73 ListMarker,
75
76 TreeItem,
77 ListBoxOption,
78 MenuItem,
79 MenuListOption,
80 Paragraph,
81
82 GenericContainer,
86
87 CheckBox,
88 RadioButton,
89 TextInput,
90 Button,
91 DefaultButton,
92 Pane,
93 RowHeader,
94 ColumnHeader,
95 RowGroup,
96 List,
97 Table,
98 LayoutTableCell,
99 LayoutTableRow,
100 LayoutTable,
101 Switch,
102 Menu,
103
104 MultilineTextInput,
105 SearchInput,
106 DateInput,
107 DateTimeInput,
108 WeekInput,
109 MonthInput,
110 TimeInput,
111 EmailInput,
112 NumberInput,
113 PasswordInput,
114 PhoneNumberInput,
115 UrlInput,
116
117 Abbr,
118 Alert,
119 AlertDialog,
120 Application,
121 Article,
122 Audio,
123 Banner,
124 Blockquote,
125 Canvas,
126 Caption,
127 Caret,
128 Code,
129 ColorWell,
130 ComboBox,
131 EditableComboBox,
132 Complementary,
133 Comment,
134 ContentDeletion,
135 ContentInsertion,
136 ContentInfo,
137 Definition,
138 DescriptionList,
139 Details,
140 Dialog,
141 DisclosureTriangle,
142 Document,
143 EmbeddedObject,
144 Emphasis,
145 Feed,
146 FigureCaption,
147 Figure,
148 Footer,
149 Form,
150 Grid,
151 GridCell,
152 Group,
153 Header,
154 Heading,
155 Iframe,
156 IframePresentational,
157 ImeCandidate,
158 Keyboard,
159 Legend,
160 LineBreak,
161 ListBox,
162 Log,
163 Main,
164 Mark,
165 Marquee,
166 Math,
167 MenuBar,
168 MenuItemCheckBox,
169 MenuItemRadio,
170 MenuListPopup,
171 Meter,
172 Navigation,
173 Note,
174 PluginObject,
175 ProgressIndicator,
176 RadioGroup,
177 Region,
178 RootWebArea,
179 Ruby,
180 RubyAnnotation,
181 ScrollBar,
182 ScrollView,
183 Search,
184 Section,
185 SectionFooter,
186 SectionHeader,
187 Slider,
188 SpinButton,
189 Splitter,
190 Status,
191 Strong,
192 Suggestion,
193 SvgRoot,
194 Tab,
195 TabList,
196 TabPanel,
197 Term,
198 Time,
199 Timer,
200 TitleBar,
201 Toolbar,
202 Tooltip,
203 Tree,
204 TreeGrid,
205 Video,
206 WebView,
207 Window,
208
209 PdfActionableHighlight,
210 PdfRoot,
211
212 GraphicsDocument,
215 GraphicsObject,
216 GraphicsSymbol,
217
218 DocAbstract,
221 DocAcknowledgements,
222 DocAfterword,
223 DocAppendix,
224 DocBackLink,
225 DocBiblioEntry,
226 DocBibliography,
227 DocBiblioRef,
228 DocChapter,
229 DocColophon,
230 DocConclusion,
231 DocCover,
232 DocCredit,
233 DocCredits,
234 DocDedication,
235 DocEndnote,
236 DocEndnotes,
237 DocEpigraph,
238 DocEpilogue,
239 DocErrata,
240 DocExample,
241 DocFootnote,
242 DocForeword,
243 DocGlossary,
244 DocGlossRef,
245 DocIndex,
246 DocIntroduction,
247 DocNoteRef,
248 DocNotice,
249 DocPageBreak,
250 DocPageFooter,
251 DocPageHeader,
252 DocPageList,
253 DocPart,
254 DocPreface,
255 DocPrologue,
256 DocPullquote,
257 DocQna,
258 DocSubtitle,
259 DocTip,
260 DocToc,
261
262 ListGrid,
266
267 Terminal,
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
275#[cfg_attr(feature = "enumn", derive(enumn::N))]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[cfg_attr(feature = "schemars", derive(JsonSchema))]
278#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
279#[cfg_attr(
280 feature = "pyo3",
281 pyclass(
282 module = "accesskit",
283 rename_all = "SCREAMING_SNAKE_CASE",
284 eq,
285 from_py_object
286 )
287)]
288#[repr(u8)]
289pub enum Action {
290 Click,
292
293 Focus,
294 Blur,
295
296 Collapse,
297 Expand,
298
299 CustomAction,
301
302 Decrement,
304 Increment,
306
307 HideTooltip,
308 ShowTooltip,
309
310 ReplaceSelectedText,
314
315 ScrollDown,
317 ScrollLeft,
319 ScrollRight,
321 ScrollUp,
323
324 ScrollIntoView,
327
328 ScrollToPoint,
332
333 SetScrollOffset,
335
336 SetTextSelection,
338
339 SetSequentialFocusNavigationStartingPoint,
343
344 SetValue,
348
349 ShowContextMenu,
350}
351
352impl Action {
353 fn mask(self) -> u32 {
354 1 << (self as u8)
355 }
356
357 #[cfg(not(feature = "enumn"))]
358 fn n(value: u8) -> Option<Self> {
359 match value {
363 0 => Some(Action::Click),
364 1 => Some(Action::Focus),
365 2 => Some(Action::Blur),
366 3 => Some(Action::Collapse),
367 4 => Some(Action::Expand),
368 5 => Some(Action::CustomAction),
369 6 => Some(Action::Decrement),
370 7 => Some(Action::Increment),
371 8 => Some(Action::HideTooltip),
372 9 => Some(Action::ShowTooltip),
373 10 => Some(Action::ReplaceSelectedText),
374 11 => Some(Action::ScrollDown),
375 12 => Some(Action::ScrollLeft),
376 13 => Some(Action::ScrollRight),
377 14 => Some(Action::ScrollUp),
378 15 => Some(Action::ScrollIntoView),
379 16 => Some(Action::ScrollToPoint),
380 17 => Some(Action::SetScrollOffset),
381 18 => Some(Action::SetTextSelection),
382 19 => Some(Action::SetSequentialFocusNavigationStartingPoint),
383 20 => Some(Action::SetValue),
384 21 => Some(Action::ShowContextMenu),
385 _ => None,
386 }
387 }
388}
389
390fn action_mask_to_action_vec(mask: u32) -> Vec<Action> {
391 let mut actions = Vec::new();
392 let mut i = 0;
393 while let Some(variant) = Action::n(i) {
394 if mask & variant.mask() != 0 {
395 actions.push(variant);
396 }
397 i += 1;
398 }
399 actions
400}
401
402#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
403#[cfg_attr(feature = "enumn", derive(enumn::N))]
404#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
405#[cfg_attr(feature = "schemars", derive(JsonSchema))]
406#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
407#[cfg_attr(
408 feature = "pyo3",
409 pyclass(
410 module = "accesskit",
411 rename_all = "SCREAMING_SNAKE_CASE",
412 eq,
413 from_py_object
414 )
415)]
416#[repr(u8)]
417pub enum Orientation {
418 Horizontal,
420 Vertical,
422}
423
424#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
425#[cfg_attr(feature = "enumn", derive(enumn::N))]
426#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
427#[cfg_attr(feature = "schemars", derive(JsonSchema))]
428#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
429#[cfg_attr(
430 feature = "pyo3",
431 pyclass(
432 module = "accesskit",
433 rename_all = "SCREAMING_SNAKE_CASE",
434 eq,
435 from_py_object
436 )
437)]
438#[repr(u8)]
439pub enum TextDirection {
440 LeftToRight,
441 RightToLeft,
442 TopToBottom,
443 BottomToTop,
444}
445
446#[derive(Clone, Copy, Debug, PartialEq, Eq)]
451#[cfg_attr(feature = "enumn", derive(enumn::N))]
452#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
453#[cfg_attr(feature = "schemars", derive(JsonSchema))]
454#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
455#[cfg_attr(
456 feature = "pyo3",
457 pyclass(
458 module = "accesskit",
459 rename_all = "SCREAMING_SNAKE_CASE",
460 eq,
461 from_py_object
462 )
463)]
464#[repr(u8)]
465pub enum Invalid {
466 True,
467 Grammar,
468 Spelling,
469}
470
471#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
472#[cfg_attr(feature = "enumn", derive(enumn::N))]
473#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
474#[cfg_attr(feature = "schemars", derive(JsonSchema))]
475#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
476#[cfg_attr(
477 feature = "pyo3",
478 pyclass(
479 module = "accesskit",
480 rename_all = "SCREAMING_SNAKE_CASE",
481 eq,
482 from_py_object
483 )
484)]
485#[repr(u8)]
486pub enum Toggled {
487 False,
488 True,
489 Mixed,
490}
491
492impl From<bool> for Toggled {
493 #[inline]
494 fn from(b: bool) -> Self {
495 match b {
496 false => Self::False,
497 true => Self::True,
498 }
499 }
500}
501
502#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
503#[cfg_attr(feature = "enumn", derive(enumn::N))]
504#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
505#[cfg_attr(feature = "schemars", derive(JsonSchema))]
506#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
507#[cfg_attr(
508 feature = "pyo3",
509 pyclass(
510 module = "accesskit",
511 rename_all = "SCREAMING_SNAKE_CASE",
512 eq,
513 from_py_object
514 )
515)]
516#[repr(u8)]
517pub enum SortDirection {
518 Ascending,
519 Descending,
520 Other,
521}
522
523#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
524#[cfg_attr(feature = "enumn", derive(enumn::N))]
525#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
526#[cfg_attr(feature = "schemars", derive(JsonSchema))]
527#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
528#[cfg_attr(
529 feature = "pyo3",
530 pyclass(
531 module = "accesskit",
532 rename_all = "SCREAMING_SNAKE_CASE",
533 eq,
534 from_py_object
535 )
536)]
537#[repr(u8)]
538pub enum AriaCurrent {
539 False,
540 True,
541 Page,
542 Step,
543 Location,
544 Date,
545 Time,
546}
547
548#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
549#[cfg_attr(feature = "enumn", derive(enumn::N))]
550#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
551#[cfg_attr(feature = "schemars", derive(JsonSchema))]
552#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
553#[cfg_attr(
554 feature = "pyo3",
555 pyclass(
556 module = "accesskit",
557 rename_all = "SCREAMING_SNAKE_CASE",
558 eq,
559 from_py_object
560 )
561)]
562#[repr(u8)]
563pub enum AutoComplete {
564 Inline,
565 List,
566 Both,
567}
568
569#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
570#[cfg_attr(feature = "enumn", derive(enumn::N))]
571#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
572#[cfg_attr(feature = "schemars", derive(JsonSchema))]
573#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
574#[cfg_attr(
575 feature = "pyo3",
576 pyclass(
577 module = "accesskit",
578 rename_all = "SCREAMING_SNAKE_CASE",
579 eq,
580 from_py_object
581 )
582)]
583#[repr(u8)]
584pub enum Live {
585 Off,
586 Polite,
587 Assertive,
588}
589
590#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
591#[cfg_attr(feature = "enumn", derive(enumn::N))]
592#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
593#[cfg_attr(feature = "schemars", derive(JsonSchema))]
594#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
595#[cfg_attr(
596 feature = "pyo3",
597 pyclass(
598 module = "accesskit",
599 rename_all = "SCREAMING_SNAKE_CASE",
600 eq,
601 from_py_object
602 )
603)]
604#[repr(u8)]
605pub enum HasPopup {
606 Menu,
607 Listbox,
608 Tree,
609 Grid,
610 Dialog,
611}
612
613#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
614#[cfg_attr(feature = "enumn", derive(enumn::N))]
615#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
616#[cfg_attr(feature = "schemars", derive(JsonSchema))]
617#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
618#[cfg_attr(
619 feature = "pyo3",
620 pyclass(
621 module = "accesskit",
622 rename_all = "SCREAMING_SNAKE_CASE",
623 eq,
624 from_py_object
625 )
626)]
627#[repr(u8)]
628pub enum ListStyle {
629 Circle,
630 Disc,
631 Image,
632 Numeric,
633 Square,
634 Other,
636}
637
638#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
639#[cfg_attr(feature = "enumn", derive(enumn::N))]
640#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
641#[cfg_attr(feature = "schemars", derive(JsonSchema))]
642#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
643#[cfg_attr(
644 feature = "pyo3",
645 pyclass(
646 module = "accesskit",
647 rename_all = "SCREAMING_SNAKE_CASE",
648 eq,
649 from_py_object
650 )
651)]
652#[repr(u8)]
653pub enum TextAlign {
654 Left,
655 Right,
656 Center,
657 Justify,
658}
659
660#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
661#[cfg_attr(feature = "enumn", derive(enumn::N))]
662#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
663#[cfg_attr(feature = "schemars", derive(JsonSchema))]
664#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
665#[cfg_attr(
666 feature = "pyo3",
667 pyclass(
668 module = "accesskit",
669 rename_all = "SCREAMING_SNAKE_CASE",
670 eq,
671 from_py_object
672 )
673)]
674#[repr(u8)]
675pub enum VerticalOffset {
676 Subscript,
677 Superscript,
678}
679
680#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
681#[cfg_attr(feature = "enumn", derive(enumn::N))]
682#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
683#[cfg_attr(feature = "schemars", derive(JsonSchema))]
684#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
685#[cfg_attr(
686 feature = "pyo3",
687 pyclass(
688 module = "accesskit",
689 rename_all = "SCREAMING_SNAKE_CASE",
690 eq,
691 from_py_object
692 )
693)]
694#[repr(u8)]
695pub enum TextDecorationStyle {
696 Solid,
697 Dotted,
698 Dashed,
699 Double,
700 Wavy,
701}
702
703pub type NodeIdContent = u64;
704
705#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
712#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
713#[cfg_attr(feature = "schemars", derive(JsonSchema))]
714#[repr(transparent)]
715pub struct NodeId(pub NodeIdContent);
716
717impl From<NodeIdContent> for NodeId {
718 #[inline]
719 fn from(inner: NodeIdContent) -> Self {
720 Self(inner)
721 }
722}
723
724impl From<NodeId> for NodeIdContent {
725 #[inline]
726 fn from(outer: NodeId) -> Self {
727 outer.0
728 }
729}
730
731impl fmt::Debug for NodeId {
732 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
733 write!(f, "#{}", self.0)
734 }
735}
736
737#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
742#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
743#[cfg_attr(feature = "schemars", derive(JsonSchema))]
744#[repr(transparent)]
745pub struct TreeId(pub Uuid);
746
747impl TreeId {
748 pub const ROOT: Self = Self(Uuid::nil());
750}
751
752#[derive(Clone, Debug, PartialEq, Eq)]
757#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
758#[cfg_attr(feature = "schemars", derive(JsonSchema))]
759#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
760#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
761pub struct CustomAction {
762 pub id: i32,
763 pub description: Box<str>,
764}
765
766#[derive(Clone, Copy, Debug, PartialEq, Eq)]
767#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
768#[cfg_attr(feature = "schemars", derive(JsonSchema))]
769#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
770#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
771pub struct TextPosition {
772 pub node: NodeId,
774 pub character_index: usize,
777}
778
779#[derive(Clone, Copy, Debug, PartialEq, Eq)]
780#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
781#[cfg_attr(feature = "schemars", derive(JsonSchema))]
782#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
783#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
784pub struct TextSelection {
785 pub anchor: TextPosition,
790 pub focus: TextPosition,
794}
795
796#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
797#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
798#[cfg_attr(feature = "schemars", derive(JsonSchema))]
799#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
800#[repr(u8)]
801enum Flag {
802 Hidden,
803 Multiselectable,
804 Required,
805 Visited,
806 Busy,
807 LiveAtomic,
808 Modal,
809 TouchTransparent,
810 ReadOnly,
811 Disabled,
812 Italic,
813 ClipsChildren,
814 IsLineBreakingObject,
815 IsPageBreakingObject,
816 IsSpellingError,
817 IsGrammarError,
818 IsSearchMatch,
819 IsSuggestion,
820}
821
822impl Flag {
823 fn mask(self) -> u32 {
824 1 << (self as u8)
825 }
826}
827
828#[derive(Clone, Copy, Debug, PartialEq, Eq)]
830#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
831#[cfg_attr(feature = "schemars", derive(JsonSchema))]
832#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
833#[repr(C)]
834pub struct Color {
835 pub red: u8,
836 pub green: u8,
837 pub blue: u8,
838 pub alpha: u8,
839}
840
841#[derive(Clone, Copy, Debug, PartialEq, Eq)]
843#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
844#[cfg_attr(feature = "schemars", derive(JsonSchema))]
845#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
846#[repr(C)]
847pub struct TextDecoration {
848 pub style: TextDecorationStyle,
849 pub color: Color,
850}
851
852#[derive(Clone, Debug, PartialEq)]
856enum PropertyValue {
857 None,
858 NodeIdVec(Vec<NodeId>),
859 NodeId(NodeId),
860 String(Box<str>),
861 F64(f64),
862 F32(f32),
863 Usize(usize),
864 Color(Color),
865 TextDecoration(TextDecoration),
866 LengthSlice(Box<[u8]>),
867 CoordSlice(Box<[f32]>),
868 Bool(bool),
869 Invalid(Invalid),
870 Toggled(Toggled),
871 Live(Live),
872 TextDirection(TextDirection),
873 Orientation(Orientation),
874 SortDirection(SortDirection),
875 AriaCurrent(AriaCurrent),
876 AutoComplete(AutoComplete),
877 HasPopup(HasPopup),
878 ListStyle(ListStyle),
879 TextAlign(TextAlign),
880 VerticalOffset(VerticalOffset),
881 Affine(Box<Affine>),
882 Rect(Rect),
883 TextSelection(Box<TextSelection>),
884 CustomActionVec(Vec<CustomAction>),
885 TreeId(TreeId),
886}
887
888#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
889#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
890#[cfg_attr(feature = "schemars", derive(JsonSchema))]
891#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
892#[repr(u8)]
893enum PropertyId {
894 Children,
896 Controls,
897 Details,
898 DescribedBy,
899 FlowTo,
900 LabelledBy,
901 Owns,
902 RadioGroup,
903
904 ActiveDescendant,
906 ErrorMessage,
907 InPageLinkTarget,
908 MemberOf,
909 NextOnLine,
910 PreviousOnLine,
911 PopupFor,
912
913 Label,
915 Description,
916 Value,
917 AccessKey,
918 AuthorId,
919 ClassName,
920 FontFamily,
921 HtmlTag,
922 InnerHtml,
923 KeyboardShortcut,
924 Language,
925 Placeholder,
926 RoleDescription,
927 StateDescription,
928 Tooltip,
929 Url,
930 RowIndexText,
931 ColumnIndexText,
932 BrailleLabel,
933 BrailleRoleDescription,
934
935 ScrollX,
937 ScrollXMin,
938 ScrollXMax,
939 ScrollY,
940 ScrollYMin,
941 ScrollYMax,
942 NumericValue,
943 MinNumericValue,
944 MaxNumericValue,
945 NumericValueStep,
946 NumericValueJump,
947
948 FontSize,
950 FontWeight,
951
952 RowCount,
954 ColumnCount,
955 RowIndex,
956 ColumnIndex,
957 RowSpan,
958 ColumnSpan,
959 Level,
960 SizeOfSet,
961 PositionInSet,
962
963 ColorValue,
965 BackgroundColor,
966 ForegroundColor,
967
968 Overline,
970 Strikethrough,
971 Underline,
972
973 CharacterLengths,
975 WordStarts,
976
977 CharacterPositions,
979 CharacterWidths,
980
981 Expanded,
983 Selected,
984
985 Invalid,
987 Toggled,
988 Live,
989 TextDirection,
990 Orientation,
991 SortDirection,
992 AriaCurrent,
993 AutoComplete,
994 HasPopup,
995 ListStyle,
996 TextAlign,
997 VerticalOffset,
998
999 Transform,
1001 Bounds,
1002 TextSelection,
1003 CustomActions,
1004 TreeId,
1005
1006 Unset,
1008}
1009
1010#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1011#[repr(transparent)]
1012struct PropertyIndices([u8; PropertyId::Unset as usize]);
1013
1014impl Default for PropertyIndices {
1015 fn default() -> Self {
1016 Self([PropertyId::Unset as u8; PropertyId::Unset as usize])
1017 }
1018}
1019
1020#[derive(Clone, Debug, Default, PartialEq)]
1021struct Properties {
1022 indices: PropertyIndices,
1023 values: Vec<PropertyValue>,
1024}
1025
1026#[derive(Clone, Default, PartialEq)]
1033#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1034#[cfg_attr(feature = "schemars", derive(JsonSchema))]
1035#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
1036#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1037pub struct Node {
1038 role: Role,
1039 actions: u32,
1040 child_actions: u32,
1041 flags: u32,
1042 properties: Properties,
1043}
1044
1045impl PropertyIndices {
1046 fn get<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a PropertyValue {
1047 let index = self.0[id as usize];
1048 if index == PropertyId::Unset as u8 {
1049 &PropertyValue::None
1050 } else {
1051 &values[index as usize]
1052 }
1053 }
1054}
1055
1056impl Properties {
1057 fn get_mut(&mut self, id: PropertyId, default: PropertyValue) -> &mut PropertyValue {
1058 let index = self.indices.0[id as usize] as usize;
1059 if index == PropertyId::Unset as usize {
1060 self.values.push(default);
1061 let index = self.values.len() - 1;
1062 self.indices.0[id as usize] = index as u8;
1063 &mut self.values[index]
1064 } else {
1065 &mut self.values[index]
1066 }
1067 }
1068
1069 fn set(&mut self, id: PropertyId, value: PropertyValue) {
1070 let index = self.indices.0[id as usize];
1071 if index == PropertyId::Unset as u8 {
1072 self.values.push(value);
1073 self.indices.0[id as usize] = (self.values.len() - 1) as u8;
1074 } else {
1075 self.values[index as usize] = value;
1076 }
1077 }
1078
1079 fn clear(&mut self, id: PropertyId) {
1080 let index = self.indices.0[id as usize];
1081 if index != PropertyId::Unset as u8 {
1082 self.values[index as usize] = PropertyValue::None;
1083 }
1084 }
1085}
1086
1087macro_rules! flag_methods {
1088 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1089 impl Node {
1090 $($(#[$doc])*
1091 #[inline]
1092 pub fn $getter(&self) -> bool {
1093 (self.flags & (Flag::$id).mask()) != 0
1094 }
1095 #[inline]
1096 pub fn $setter(&mut self) {
1097 self.flags |= (Flag::$id).mask();
1098 }
1099 #[inline]
1100 pub fn $clearer(&mut self) {
1101 self.flags &= !((Flag::$id).mask());
1102 })*
1103 fn debug_flag_properties(&self, fmt: &mut fmt::DebugStruct) {
1104 $(
1105 if self.$getter() {
1106 fmt.field(stringify!($getter), &true);
1107 }
1108 )*
1109 }
1110 }
1111 $(#[cfg(test)]
1112 mod $getter {
1113 use super::{Node, Role};
1114
1115 #[test]
1116 fn getter_should_return_default_value() {
1117 let node = Node::new(Role::Unknown);
1118 assert!(!node.$getter());
1119 }
1120
1121 #[test]
1122 fn setter_should_update_the_property() {
1123 let mut node = Node::new(Role::Unknown);
1124 node.$setter();
1125 assert!(node.$getter());
1126 }
1127
1128 #[test]
1129 fn clearer_should_reset_the_property() {
1130 let mut node = Node::new(Role::Unknown);
1131 node.$setter();
1132 node.$clearer();
1133 assert!(!node.$getter());
1134 }
1135 })*
1136 }
1137}
1138
1139macro_rules! option_ref_type_getters {
1140 ($(($method:ident, $type:ty, $variant:ident)),+) => {
1141 impl PropertyIndices {
1142 $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> Option<&'a $type> {
1143 match self.get(values, id) {
1144 PropertyValue::$variant(value) => Some(value),
1145 _ => None,
1146 }
1147 })*
1148 }
1149 }
1150}
1151
1152macro_rules! slice_type_getters {
1153 ($(($method:ident, $type:ty, $variant:ident)),+) => {
1154 impl PropertyIndices {
1155 $(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a [$type] {
1156 match self.get(values, id) {
1157 PropertyValue::$variant(value) => value,
1158 _ => &[],
1159 }
1160 })*
1161 }
1162 }
1163}
1164
1165macro_rules! copy_type_getters {
1166 ($(($method:ident, $type:ty, $variant:ident)),+) => {
1167 impl PropertyIndices {
1168 $(fn $method(&self, values: &[PropertyValue], id: PropertyId) -> Option<$type> {
1169 match self.get(values, id) {
1170 PropertyValue::$variant(value) => Some(*value),
1171 _ => None,
1172 }
1173 })*
1174 }
1175 }
1176}
1177
1178macro_rules! box_type_setters {
1179 ($(($method:ident, $type:ty, $variant:ident)),+) => {
1180 impl Node {
1181 $(fn $method(&mut self, id: PropertyId, value: impl Into<Box<$type>>) {
1182 self.properties.set(id, PropertyValue::$variant(value.into()));
1183 })*
1184 }
1185 }
1186}
1187
1188macro_rules! copy_type_setters {
1189 ($(($method:ident, $type:ty, $variant:ident)),+) => {
1190 impl Node {
1191 $(fn $method(&mut self, id: PropertyId, value: $type) {
1192 self.properties.set(id, PropertyValue::$variant(value));
1193 })*
1194 }
1195 }
1196}
1197
1198macro_rules! vec_type_methods {
1199 ($(($type:ty, $variant:ident, $getter:ident, $setter:ident, $pusher:ident)),+) => {
1200 $(slice_type_getters! {
1201 ($getter, $type, $variant)
1202 })*
1203 impl Node {
1204 $(fn $setter(&mut self, id: PropertyId, value: impl Into<Vec<$type>>) {
1205 self.properties.set(id, PropertyValue::$variant(value.into()));
1206 }
1207 fn $pusher(&mut self, id: PropertyId, item: $type) {
1208 if let PropertyValue::$variant(v) = self.properties.get_mut(id, PropertyValue::$variant(Vec::new())) {
1209 v.push(item);
1210 }
1211 })*
1212 }
1213 }
1214}
1215
1216macro_rules! property_methods {
1217 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $type_getter:ident, $getter_result:ty, $setter:ident, $type_setter:ident, $setter_param:ty, $clearer:ident)),+) => {
1218 impl Node {
1219 $($(#[$doc])*
1220 #[inline]
1221 pub fn $getter(&self) -> $getter_result {
1222 self.properties.indices.$type_getter(&self.properties.values, PropertyId::$id)
1223 }
1224 #[inline]
1225 pub fn $setter(&mut self, value: $setter_param) {
1226 self.$type_setter(PropertyId::$id, value);
1227 }
1228 #[inline]
1229 pub fn $clearer(&mut self) {
1230 self.properties.clear(PropertyId::$id);
1231 })*
1232 }
1233 }
1234}
1235
1236macro_rules! vec_property_methods {
1237 ($($(#[$doc:meta])* ($id:ident, $item_type:ty, $getter:ident, $type_getter:ident, $setter:ident, $type_setter:ident, $pusher:ident, $type_pusher:ident, $clearer:ident)),+) => {
1238 $(property_methods! {
1239 $(#[$doc])*
1240 ($id, $getter, $type_getter, &[$item_type], $setter, $type_setter, impl Into<Vec<$item_type>>, $clearer)
1241 }
1242 impl Node {
1243 #[inline]
1244 pub fn $pusher(&mut self, item: $item_type) {
1245 self.$type_pusher(PropertyId::$id, item);
1246 }
1247 })*
1248 }
1249}
1250
1251macro_rules! slice_properties_debug_method {
1252 ($name:ident, [$($getter:ident,)*]) => {
1253 fn $name(&self, fmt: &mut fmt::DebugStruct) {
1254 $(
1255 let value = self.$getter();
1256 if !value.is_empty() {
1257 fmt.field(stringify!($getter), &value);
1258 }
1259 )*
1260 }
1261 }
1262}
1263
1264macro_rules! node_id_vec_property_methods {
1265 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $pusher:ident, $clearer:ident)),+) => {
1266 $(vec_property_methods! {
1267 $(#[$doc])*
1268 ($id, NodeId, $getter, get_node_id_vec, $setter, set_node_id_vec, $pusher, push_to_node_id_vec, $clearer)
1269 })*
1270 impl Node {
1271 slice_properties_debug_method! { debug_node_id_vec_properties, [$($getter,)*] }
1272 }
1273 $(#[cfg(test)]
1274 mod $getter {
1275 use super::{Node, NodeId, Role};
1276
1277 #[test]
1278 fn getter_should_return_default_value() {
1279 let node = Node::new(Role::Unknown);
1280 assert!(node.$getter().is_empty());
1281 }
1282 #[test]
1283 fn setter_should_update_the_property() {
1284 let mut node = Node::new(Role::Unknown);
1285 node.$setter([]);
1286 assert!(node.$getter().is_empty());
1287 node.$setter([NodeId(0), NodeId(1)]);
1288 assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1289 }
1290 #[test]
1291 fn pusher_should_update_the_property() {
1292 let mut node = Node::new(Role::Unknown);
1293 node.$pusher(NodeId(0));
1294 assert_eq!(node.$getter(), &[NodeId(0)]);
1295 node.$pusher(NodeId(1));
1296 assert_eq!(node.$getter(), &[NodeId(0), NodeId(1)]);
1297 }
1298 #[test]
1299 fn clearer_should_reset_the_property() {
1300 let mut node = Node::new(Role::Unknown);
1301 node.$setter([NodeId(0)]);
1302 node.$clearer();
1303 assert!(node.$getter().is_empty());
1304 }
1305 })*
1306 }
1307}
1308
1309macro_rules! option_properties_debug_method {
1310 ($name:ident, [$($getter:ident,)*]) => {
1311 fn $name(&self, fmt: &mut fmt::DebugStruct) {
1312 $(
1313 if let Some(value) = self.$getter() {
1314 fmt.field(stringify!($getter), &value);
1315 }
1316 )*
1317 }
1318 }
1319}
1320
1321macro_rules! node_id_property_methods {
1322 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1323 $(property_methods! {
1324 $(#[$doc])*
1325 ($id, $getter, get_node_id_property, Option<NodeId>, $setter, set_node_id_property, NodeId, $clearer)
1326 })*
1327 impl Node {
1328 option_properties_debug_method! { debug_node_id_properties, [$($getter,)*] }
1329 }
1330 $(#[cfg(test)]
1331 mod $getter {
1332 use super::{Node, NodeId, Role};
1333
1334 #[test]
1335 fn getter_should_return_default_value() {
1336 let node = Node::new(Role::Unknown);
1337 assert!(node.$getter().is_none());
1338 }
1339 #[test]
1340 fn setter_should_update_the_property() {
1341 let mut node = Node::new(Role::Unknown);
1342 node.$setter(NodeId(1));
1343 assert_eq!(node.$getter(), Some(NodeId(1)));
1344 }
1345 #[test]
1346 fn clearer_should_reset_the_property() {
1347 let mut node = Node::new(Role::Unknown);
1348 node.$setter(NodeId(1));
1349 node.$clearer();
1350 assert!(node.$getter().is_none());
1351 }
1352 })*
1353 }
1354}
1355
1356macro_rules! string_property_methods {
1357 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1358 $(property_methods! {
1359 $(#[$doc])*
1360 ($id, $getter, get_string_property, Option<&str>, $setter, set_string_property, impl Into<Box<str>>, $clearer)
1361 })*
1362 impl Node {
1363 option_properties_debug_method! { debug_string_properties, [$($getter,)*] }
1364 }
1365 $(#[cfg(test)]
1366 mod $getter {
1367 use super::{Node, Role};
1368
1369 #[test]
1370 fn getter_should_return_default_value() {
1371 let node = Node::new(Role::Unknown);
1372 assert!(node.$getter().is_none());
1373 }
1374 #[test]
1375 fn setter_should_update_the_property() {
1376 let mut node = Node::new(Role::Unknown);
1377 node.$setter("test");
1378 assert_eq!(node.$getter(), Some("test"));
1379 }
1380 #[test]
1381 fn clearer_should_reset_the_property() {
1382 let mut node = Node::new(Role::Unknown);
1383 node.$setter("test");
1384 node.$clearer();
1385 assert!(node.$getter().is_none());
1386 }
1387 })*
1388 }
1389}
1390
1391macro_rules! f64_property_methods {
1392 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1393 $(property_methods! {
1394 $(#[$doc])*
1395 ($id, $getter, get_f64_property, Option<f64>, $setter, set_f64_property, f64, $clearer)
1396 })*
1397 impl Node {
1398 option_properties_debug_method! { debug_f64_properties, [$($getter,)*] }
1399 }
1400 $(#[cfg(test)]
1401 mod $getter {
1402 use super::{Node, Role};
1403
1404 #[test]
1405 fn getter_should_return_default_value() {
1406 let node = Node::new(Role::Unknown);
1407 assert!(node.$getter().is_none());
1408 }
1409 #[test]
1410 fn setter_should_update_the_property() {
1411 let mut node = Node::new(Role::Unknown);
1412 node.$setter(1.0);
1413 assert_eq!(node.$getter(), Some(1.0));
1414 }
1415 #[test]
1416 fn clearer_should_reset_the_property() {
1417 let mut node = Node::new(Role::Unknown);
1418 node.$setter(1.0);
1419 node.$clearer();
1420 assert!(node.$getter().is_none());
1421 }
1422 })*
1423 }
1424}
1425
1426macro_rules! f32_property_methods {
1427 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1428 $(property_methods! {
1429 $(#[$doc])*
1430 ($id, $getter, get_f32_property, Option<f32>, $setter, set_f32_property, f32, $clearer)
1431 })*
1432 impl Node {
1433 option_properties_debug_method! { debug_f32_properties, [$($getter,)*] }
1434 }
1435 $(#[cfg(test)]
1436 mod $getter {
1437 use super::{Node, Role};
1438
1439 #[test]
1440 fn getter_should_return_default_value() {
1441 let node = Node::new(Role::Unknown);
1442 assert!(node.$getter().is_none());
1443 }
1444 #[test]
1445 fn setter_should_update_the_property() {
1446 let mut node = Node::new(Role::Unknown);
1447 node.$setter(1.0);
1448 assert_eq!(node.$getter(), Some(1.0));
1449 }
1450 #[test]
1451 fn clearer_should_reset_the_property() {
1452 let mut node = Node::new(Role::Unknown);
1453 node.$setter(1.0);
1454 node.$clearer();
1455 assert!(node.$getter().is_none());
1456 }
1457 })*
1458 }
1459}
1460
1461macro_rules! usize_property_methods {
1462 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1463 $(property_methods! {
1464 $(#[$doc])*
1465 ($id, $getter, get_usize_property, Option<usize>, $setter, set_usize_property, usize, $clearer)
1466 })*
1467 impl Node {
1468 option_properties_debug_method! { debug_usize_properties, [$($getter,)*] }
1469 }
1470 $(#[cfg(test)]
1471 mod $getter {
1472 use super::{Node, Role};
1473
1474 #[test]
1475 fn getter_should_return_default_value() {
1476 let node = Node::new(Role::Unknown);
1477 assert!(node.$getter().is_none());
1478 }
1479 #[test]
1480 fn setter_should_update_the_property() {
1481 let mut node = Node::new(Role::Unknown);
1482 node.$setter(1);
1483 assert_eq!(node.$getter(), Some(1));
1484 }
1485 #[test]
1486 fn clearer_should_reset_the_property() {
1487 let mut node = Node::new(Role::Unknown);
1488 node.$setter(1);
1489 node.$clearer();
1490 assert!(node.$getter().is_none());
1491 }
1492 })*
1493 }
1494}
1495
1496macro_rules! color_property_methods {
1497 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1498 $(property_methods! {
1499 $(#[$doc])*
1500 ($id, $getter, get_color_property, Option<Color>, $setter, set_color_property, Color, $clearer)
1501 })*
1502 impl Node {
1503 option_properties_debug_method! { debug_color_properties, [$($getter,)*] }
1504 }
1505 $(#[cfg(test)]
1506 mod $getter {
1507 use super::{Color, Node, Role};
1508
1509 #[test]
1510 fn getter_should_return_default_value() {
1511 let node = Node::new(Role::Unknown);
1512 assert!(node.$getter().is_none());
1513 }
1514 #[test]
1515 fn setter_should_update_the_property() {
1516 let mut node = Node::new(Role::Unknown);
1517 node.$setter(Color { red: 255, green: 255, blue: 255, alpha: 255 });
1518 assert_eq!(node.$getter(), Some(Color { red: 255, green: 255, blue: 255, alpha: 255 }));
1519 }
1520 #[test]
1521 fn clearer_should_reset_the_property() {
1522 let mut node = Node::new(Role::Unknown);
1523 node.$setter(Color { red: 255, green: 255, blue: 255, alpha: 255 });
1524 node.$clearer();
1525 assert!(node.$getter().is_none());
1526 }
1527 })*
1528 }
1529}
1530
1531macro_rules! text_decoration_property_methods {
1532 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1533 $(property_methods! {
1534 $(#[$doc])*
1535 ($id, $getter, get_text_decoration_property, Option<TextDecoration>, $setter, set_text_decoration_property, TextDecoration, $clearer)
1536 })*
1537 impl Node {
1538 option_properties_debug_method! { debug_text_decoration_properties, [$($getter,)*] }
1539 }
1540 $(#[cfg(test)]
1541 mod $getter {
1542 use super::{Color, Node, Role, TextDecoration, TextDecorationStyle};
1543
1544 const TEST_TEXT_DECORATION: TextDecoration = TextDecoration {
1545 style: TextDecorationStyle::Dotted,
1546 color: Color {
1547 red: 0,
1548 green: 0,
1549 blue: 0,
1550 alpha: 255,
1551 },
1552 };
1553
1554 #[test]
1555 fn getter_should_return_default_value() {
1556 let node = Node::new(Role::Unknown);
1557 assert!(node.$getter().is_none());
1558 }
1559 #[test]
1560 fn setter_should_update_the_property() {
1561 let mut node = Node::new(Role::Unknown);
1562 node.$setter(TEST_TEXT_DECORATION);
1563 assert_eq!(node.$getter(), Some(TEST_TEXT_DECORATION));
1564 }
1565 #[test]
1566 fn clearer_should_reset_the_property() {
1567 let mut node = Node::new(Role::Unknown);
1568 node.$setter(TEST_TEXT_DECORATION);
1569 node.$clearer();
1570 assert!(node.$getter().is_none());
1571 }
1572 })*
1573 }
1574}
1575
1576macro_rules! length_slice_property_methods {
1577 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1578 $(property_methods! {
1579 $(#[$doc])*
1580 ($id, $getter, get_length_slice_property, &[u8], $setter, set_length_slice_property, impl Into<Box<[u8]>>, $clearer)
1581 })*
1582 impl Node {
1583 slice_properties_debug_method! { debug_length_slice_properties, [$($getter,)*] }
1584 }
1585 $(#[cfg(test)]
1586 mod $getter {
1587 use super::{Node, Role};
1588
1589 #[test]
1590 fn getter_should_return_default_value() {
1591 let node = Node::new(Role::Unknown);
1592 assert!(node.$getter().is_empty());
1593 }
1594 #[test]
1595 fn setter_should_update_the_property() {
1596 let mut node = Node::new(Role::Unknown);
1597 node.$setter([]);
1598 assert!(node.$getter().is_empty());
1599 node.$setter([1, 2]);
1600 assert_eq!(node.$getter(), &[1, 2]);
1601 }
1602 #[test]
1603 fn clearer_should_reset_the_property() {
1604 let mut node = Node::new(Role::Unknown);
1605 node.$setter([1, 2]);
1606 node.$clearer();
1607 assert!(node.$getter().is_empty());
1608 }
1609 })*
1610 }
1611}
1612
1613macro_rules! coord_slice_property_methods {
1614 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1615 $(property_methods! {
1616 $(#[$doc])*
1617 ($id, $getter, get_coord_slice_property, Option<&[f32]>, $setter, set_coord_slice_property, impl Into<Box<[f32]>>, $clearer)
1618 })*
1619 impl Node {
1620 option_properties_debug_method! { debug_coord_slice_properties, [$($getter,)*] }
1621 }
1622 $(#[cfg(test)]
1623 mod $getter {
1624 use super::{Node, Role};
1625
1626 #[test]
1627 fn getter_should_return_default_value() {
1628 let node = Node::new(Role::Unknown);
1629 assert!(node.$getter().is_none());
1630 }
1631 #[test]
1632 fn setter_should_update_the_property() {
1633 let mut node = Node::new(Role::Unknown);
1634 node.$setter([]);
1635 let expected: Option<&[f32]> = Some(&[]);
1636 assert_eq!(node.$getter(), expected);
1637 node.$setter([1.0, 2.0]);
1638 let expected: Option<&[f32]> = Some(&[1.0, 2.0]);
1639 assert_eq!(node.$getter(), expected);
1640 }
1641 #[test]
1642 fn clearer_should_reset_the_property() {
1643 let mut node = Node::new(Role::Unknown);
1644 node.$setter([1.0, 2.0]);
1645 node.$clearer();
1646 assert!(node.$getter().is_none());
1647 }
1648 })*
1649 }
1650}
1651
1652macro_rules! bool_property_methods {
1653 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
1654 $(property_methods! {
1655 $(#[$doc])*
1656 ($id, $getter, get_bool_property, Option<bool>, $setter, set_bool_property, bool, $clearer)
1657 })*
1658 impl Node {
1659 option_properties_debug_method! { debug_bool_properties, [$($getter,)*] }
1660 }
1661 $(#[cfg(test)]
1662 mod $getter {
1663 use super::{Node, Role};
1664
1665 #[test]
1666 fn getter_should_return_default_value() {
1667 let node = Node::new(Role::Unknown);
1668 assert!(node.$getter().is_none());
1669 }
1670 #[test]
1671 fn setter_should_update_the_property() {
1672 let mut node = Node::new(Role::Unknown);
1673 node.$setter(true);
1674 assert_eq!(node.$getter(), Some(true));
1675 }
1676 #[test]
1677 fn clearer_should_reset_the_property() {
1678 let mut node = Node::new(Role::Unknown);
1679 node.$setter(true);
1680 node.$clearer();
1681 assert!(node.$getter().is_none());
1682 }
1683 })*
1684 }
1685}
1686
1687macro_rules! unique_enum_property_methods {
1688 ($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident, $variant:ident)),+) => {
1689 impl Node {
1690 $($(#[$doc])*
1691 #[inline]
1692 pub fn $getter(&self) -> Option<$id> {
1693 match self.properties.indices.get(&self.properties.values, PropertyId::$id) {
1694 PropertyValue::$id(value) => Some(*value),
1695 _ => None,
1696 }
1697 }
1698 #[inline]
1699 pub fn $setter(&mut self, value: $id) {
1700 self.properties.set(PropertyId::$id, PropertyValue::$id(value));
1701 }
1702 #[inline]
1703 pub fn $clearer(&mut self) {
1704 self.properties.clear(PropertyId::$id);
1705 })*
1706 option_properties_debug_method! { debug_unique_enum_properties, [$($getter,)*] }
1707 }
1708 $(#[cfg(test)]
1709 mod $getter {
1710 use super::{Node, Role};
1711
1712 #[test]
1713 fn getter_should_return_default_value() {
1714 let node = Node::new(Role::Unknown);
1715 assert!(node.$getter().is_none());
1716 }
1717 #[test]
1718 fn setter_should_update_the_property() {
1719 let mut node = Node::new(Role::Unknown);
1720 let variant = super::$id::$variant;
1721 node.$setter(variant);
1722 assert_eq!(node.$getter(), Some(variant));
1723 }
1724 #[test]
1725 fn clearer_should_reset_the_property() {
1726 let mut node = Node::new(Role::Unknown);
1727 node.$setter(super::$id::$variant);
1728 node.$clearer();
1729 assert!(node.$getter().is_none());
1730 }
1731 })*
1732 }
1733}
1734
1735impl Node {
1736 #[inline]
1737 pub fn new(role: Role) -> Self {
1738 Self {
1739 role,
1740 ..Default::default()
1741 }
1742 }
1743}
1744
1745impl Node {
1746 #[inline]
1747 pub fn role(&self) -> Role {
1748 self.role
1749 }
1750 #[inline]
1751 pub fn set_role(&mut self, value: Role) {
1752 self.role = value;
1753 }
1754
1755 #[inline]
1756 pub fn supports_action(&self, action: Action) -> bool {
1757 (self.actions & action.mask()) != 0
1758 }
1759 #[inline]
1760 pub fn add_action(&mut self, action: Action) {
1761 self.actions |= action.mask();
1762 }
1763 #[inline]
1764 pub fn remove_action(&mut self, action: Action) {
1765 self.actions &= !(action.mask());
1766 }
1767 #[inline]
1768 pub fn clear_actions(&mut self) {
1769 self.actions = 0;
1770 }
1771
1772 #[inline]
1775 pub fn child_supports_action(&self, action: Action) -> bool {
1776 (self.child_actions & action.mask()) != 0
1777 }
1778 #[inline]
1781 pub fn add_child_action(&mut self, action: Action) {
1782 self.child_actions |= action.mask();
1783 }
1784 #[inline]
1787 pub fn remove_child_action(&mut self, action: Action) {
1788 self.child_actions &= !(action.mask());
1789 }
1790 #[inline]
1793 pub fn clear_child_actions(&mut self) {
1794 self.child_actions = 0;
1795 }
1796}
1797
1798flag_methods! {
1799 (Hidden, is_hidden, set_hidden, clear_hidden),
1802 (Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
1803 (Required, is_required, set_required, clear_required),
1804 (Visited, is_visited, set_visited, clear_visited),
1805 (Busy, is_busy, set_busy, clear_busy),
1806 (LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
1807 (Modal, is_modal, set_modal, clear_modal),
1809 (TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
1813 (ReadOnly, is_read_only, set_read_only, clear_read_only),
1815 (Disabled, is_disabled, set_disabled, clear_disabled),
1817 (Italic, is_italic, set_italic, clear_italic),
1818 (ClipsChildren, clips_children, set_clips_children, clear_clips_children),
1821 (IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
1824 (IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
1826 (IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
1827 (IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
1828 (IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
1829 (IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
1830}
1831
1832option_ref_type_getters! {
1833 (get_affine_property, Affine, Affine),
1834 (get_string_property, str, String),
1835 (get_coord_slice_property, [f32], CoordSlice),
1836 (get_text_selection_property, TextSelection, TextSelection)
1837}
1838
1839slice_type_getters! {
1840 (get_length_slice_property, u8, LengthSlice)
1841}
1842
1843copy_type_getters! {
1844 (get_rect_property, Rect, Rect),
1845 (get_node_id_property, NodeId, NodeId),
1846 (get_f64_property, f64, F64),
1847 (get_f32_property, f32, F32),
1848 (get_usize_property, usize, Usize),
1849 (get_color_property, Color, Color),
1850 (get_text_decoration_property, TextDecoration, TextDecoration),
1851 (get_bool_property, bool, Bool),
1852 (get_tree_id_property, TreeId, TreeId)
1853}
1854
1855box_type_setters! {
1856 (set_affine_property, Affine, Affine),
1857 (set_string_property, str, String),
1858 (set_length_slice_property, [u8], LengthSlice),
1859 (set_coord_slice_property, [f32], CoordSlice),
1860 (set_text_selection_property, TextSelection, TextSelection)
1861}
1862
1863copy_type_setters! {
1864 (set_rect_property, Rect, Rect),
1865 (set_node_id_property, NodeId, NodeId),
1866 (set_f64_property, f64, F64),
1867 (set_f32_property, f32, F32),
1868 (set_usize_property, usize, Usize),
1869 (set_color_property, Color, Color),
1870 (set_text_decoration_property, TextDecoration, TextDecoration),
1871 (set_bool_property, bool, Bool),
1872 (set_tree_id_property, TreeId, TreeId)
1873}
1874
1875vec_type_methods! {
1876 (NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
1877 (CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
1878}
1879
1880node_id_vec_property_methods! {
1881 (Children, children, set_children, push_child, clear_children),
1882 (Controls, controls, set_controls, push_controlled, clear_controls),
1883 (Details, details, set_details, push_detail, clear_details),
1884 (DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
1885 (FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
1886 (LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
1887 (Owns, owns, set_owns, push_owned, clear_owns),
1893 (RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
1896}
1897
1898node_id_property_methods! {
1899 (ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
1903 (ErrorMessage, error_message, set_error_message, clear_error_message),
1904 (InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
1905 (MemberOf, member_of, set_member_of, clear_member_of),
1906 (NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
1907 (PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
1908 (PopupFor, popup_for, set_popup_for, clear_popup_for)
1909}
1910
1911string_property_methods! {
1912 (Label, label, set_label, clear_label),
1917 (Description, description, set_description, clear_description),
1918 (Value, value, set_value, clear_value),
1919 (AccessKey, access_key, set_access_key, clear_access_key),
1927 (AuthorId, author_id, set_author_id, clear_author_id),
1930 (ClassName, class_name, set_class_name, clear_class_name),
1931 (FontFamily, font_family, set_font_family, clear_font_family),
1933 (HtmlTag, html_tag, set_html_tag, clear_html_tag),
1934 (InnerHtml, inner_html, set_inner_html, clear_inner_html),
1937 (KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
1941 (Language, language, set_language, clear_language),
1944 (Placeholder, placeholder, set_placeholder, clear_placeholder),
1949 (RoleDescription, role_description, set_role_description, clear_role_description),
1953 (StateDescription, state_description, set_state_description, clear_state_description),
1958 (Tooltip, tooltip, set_tooltip, clear_tooltip),
1963 (Url, url, set_url, clear_url),
1964 (RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
1965 (ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text),
1966 (BrailleLabel, braille_label, set_braille_label, clear_braille_label),
1967 (BrailleRoleDescription, braille_role_description, set_braille_role_description, clear_braille_role_description)
1968}
1969
1970f64_property_methods! {
1971 (ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
1972 (ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
1973 (ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
1974 (ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
1975 (ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
1976 (ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
1977 (NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
1978 (MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
1979 (MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
1980 (NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
1981 (NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump)
1982}
1983
1984f32_property_methods! {
1985 (FontSize, font_size, set_font_size, clear_font_size),
1987 (FontWeight, font_weight, set_font_weight, clear_font_weight)
1990}
1991
1992usize_property_methods! {
1993 (RowCount, row_count, set_row_count, clear_row_count),
1994 (ColumnCount, column_count, set_column_count, clear_column_count),
1995 (RowIndex, row_index, set_row_index, clear_row_index),
1996 (ColumnIndex, column_index, set_column_index, clear_column_index),
1997 (RowSpan, row_span, set_row_span, clear_row_span),
1998 (ColumnSpan, column_span, set_column_span, clear_column_span),
1999 (Level, level, set_level, clear_level),
2000 (SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
2002 (PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
2007}
2008
2009color_property_methods! {
2010 (ColorValue, color_value, set_color_value, clear_color_value),
2012 (BackgroundColor, background_color, set_background_color, clear_background_color),
2014 (ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
2016}
2017
2018text_decoration_property_methods! {
2019 (Overline, overline, set_overline, clear_overline),
2020 (Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
2021 (Underline, underline, set_underline, clear_underline)
2022}
2023
2024length_slice_property_methods! {
2025 (CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
2044
2045 (WordStarts, word_starts, set_word_starts, clear_word_starts)
2075}
2076
2077coord_slice_property_methods! {
2078 (CharacterPositions, character_positions, set_character_positions, clear_character_positions),
2095
2096 (CharacterWidths, character_widths, set_character_widths, clear_character_widths)
2115}
2116
2117bool_property_methods! {
2118 (Expanded, is_expanded, set_expanded, clear_expanded),
2123
2124 (Selected, is_selected, set_selected, clear_selected)
2134}
2135
2136unique_enum_property_methods! {
2137 (Invalid, invalid, set_invalid, clear_invalid, Grammar),
2138 (Toggled, toggled, set_toggled, clear_toggled, True),
2139 (Live, live, set_live, clear_live, Polite),
2140 (TextDirection, text_direction, set_text_direction, clear_text_direction, RightToLeft),
2141 (Orientation, orientation, set_orientation, clear_orientation, Vertical),
2142 (SortDirection, sort_direction, set_sort_direction, clear_sort_direction, Descending),
2143 (AriaCurrent, aria_current, set_aria_current, clear_aria_current, True),
2144 (AutoComplete, auto_complete, set_auto_complete, clear_auto_complete, List),
2145 (HasPopup, has_popup, set_has_popup, clear_has_popup, Menu),
2146 (ListStyle, list_style, set_list_style, clear_list_style, Disc),
2148 (TextAlign, text_align, set_text_align, clear_text_align, Right),
2149 (VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset, Superscript)
2150}
2151
2152property_methods! {
2153 (Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
2166
2167 (Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
2178
2179 (TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection),
2180
2181 (TreeId, tree_id, get_tree_id_property, Option<TreeId>, set_tree_id, set_tree_id_property, TreeId, clear_tree_id)
2189}
2190
2191impl Node {
2192 option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection, tree_id,] }
2193}
2194
2195#[cfg(test)]
2196mod transform {
2197 use super::{Affine, Node, Role};
2198
2199 #[test]
2200 fn getter_should_return_default_value() {
2201 let node = Node::new(Role::Unknown);
2202 assert!(node.transform().is_none());
2203 }
2204 #[test]
2205 fn setter_should_update_the_property() {
2206 let mut node = Node::new(Role::Unknown);
2207 node.set_transform(Affine::IDENTITY);
2208 assert_eq!(node.transform(), Some(&Affine::IDENTITY));
2209 }
2210 #[test]
2211 fn clearer_should_reset_the_property() {
2212 let mut node = Node::new(Role::Unknown);
2213 node.set_transform(Affine::IDENTITY);
2214 node.clear_transform();
2215 assert!(node.transform().is_none());
2216 }
2217}
2218
2219#[cfg(test)]
2220mod bounds {
2221 use super::{Node, Rect, Role};
2222
2223 #[test]
2224 fn getter_should_return_default_value() {
2225 let node = Node::new(Role::Unknown);
2226 assert!(node.bounds().is_none());
2227 }
2228 #[test]
2229 fn setter_should_update_the_property() {
2230 let mut node = Node::new(Role::Unknown);
2231 let value = Rect {
2232 x0: 0.0,
2233 y0: 1.0,
2234 x1: 2.0,
2235 y1: 3.0,
2236 };
2237 node.set_bounds(value);
2238 assert_eq!(node.bounds(), Some(value));
2239 }
2240 #[test]
2241 fn clearer_should_reset_the_property() {
2242 let mut node = Node::new(Role::Unknown);
2243 node.set_bounds(Rect {
2244 x0: 0.0,
2245 y0: 1.0,
2246 x1: 2.0,
2247 y1: 3.0,
2248 });
2249 node.clear_bounds();
2250 assert!(node.bounds().is_none());
2251 }
2252}
2253
2254#[cfg(test)]
2255mod text_selection {
2256 use super::{Node, NodeId, Role, TextPosition, TextSelection};
2257
2258 #[test]
2259 fn getter_should_return_default_value() {
2260 let node = Node::new(Role::Unknown);
2261 assert!(node.text_selection().is_none());
2262 }
2263 #[test]
2264 fn setter_should_update_the_property() {
2265 let mut node = Node::new(Role::Unknown);
2266 let value = TextSelection {
2267 anchor: TextPosition {
2268 node: NodeId(0),
2269 character_index: 0,
2270 },
2271 focus: TextPosition {
2272 node: NodeId(0),
2273 character_index: 2,
2274 },
2275 };
2276 node.set_text_selection(value);
2277 assert_eq!(node.text_selection(), Some(&value));
2278 }
2279 #[test]
2280 fn clearer_should_reset_the_property() {
2281 let mut node = Node::new(Role::Unknown);
2282 node.set_text_selection(TextSelection {
2283 anchor: TextPosition {
2284 node: NodeId(0),
2285 character_index: 0,
2286 },
2287 focus: TextPosition {
2288 node: NodeId(0),
2289 character_index: 2,
2290 },
2291 });
2292 node.clear_text_selection();
2293 assert!(node.text_selection().is_none());
2294 }
2295}
2296
2297#[cfg(test)]
2298mod tree_id {
2299 use super::{Node, Role, TreeId, Uuid};
2300
2301 #[test]
2302 fn getter_should_return_default_value() {
2303 let node = Node::new(Role::GenericContainer);
2304 assert!(node.tree_id().is_none());
2305 }
2306 #[test]
2307 fn setter_should_update_the_property() {
2308 let mut node = Node::new(Role::GenericContainer);
2309 let value = TreeId(Uuid::nil());
2310 node.set_tree_id(value);
2311 assert_eq!(node.tree_id(), Some(value));
2312 }
2313 #[test]
2314 fn clearer_should_reset_the_property() {
2315 let mut node = Node::new(Role::GenericContainer);
2316 node.set_tree_id(TreeId(Uuid::nil()));
2317 node.clear_tree_id();
2318 assert!(node.tree_id().is_none());
2319 }
2320}
2321
2322vec_property_methods! {
2323 (CustomActions, CustomAction, custom_actions, get_custom_action_vec, set_custom_actions, set_custom_action_vec, push_custom_action, push_to_custom_action_vec, clear_custom_actions)
2324}
2325
2326#[cfg(test)]
2327mod custom_actions {
2328 use super::{CustomAction, Node, Role};
2329 use core::slice;
2330
2331 #[test]
2332 fn getter_should_return_default_value() {
2333 let node = Node::new(Role::Unknown);
2334 assert!(node.custom_actions().is_empty());
2335 }
2336 #[test]
2337 fn setter_should_update_the_property() {
2338 let mut node = Node::new(Role::Unknown);
2339 let value = alloc::vec![
2340 CustomAction {
2341 id: 0,
2342 description: "first test action".into(),
2343 },
2344 CustomAction {
2345 id: 1,
2346 description: "second test action".into(),
2347 },
2348 ];
2349 node.set_custom_actions(value.clone());
2350 assert_eq!(node.custom_actions(), value);
2351 }
2352 #[test]
2353 fn pusher_should_update_the_property() {
2354 let mut node = Node::new(Role::Unknown);
2355 let first_action = CustomAction {
2356 id: 0,
2357 description: "first test action".into(),
2358 };
2359 let second_action = CustomAction {
2360 id: 1,
2361 description: "second test action".into(),
2362 };
2363 node.push_custom_action(first_action.clone());
2364 assert_eq!(node.custom_actions(), slice::from_ref(&first_action));
2365 node.push_custom_action(second_action.clone());
2366 assert_eq!(node.custom_actions(), &[first_action, second_action]);
2367 }
2368 #[test]
2369 fn clearer_should_reset_the_property() {
2370 let mut node = Node::new(Role::Unknown);
2371 node.set_custom_actions([CustomAction {
2372 id: 0,
2373 description: "test action".into(),
2374 }]);
2375 node.clear_custom_actions();
2376 assert!(node.custom_actions().is_empty());
2377 }
2378}
2379
2380impl fmt::Debug for Node {
2381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2382 let mut fmt = f.debug_struct("Node");
2383
2384 fmt.field("role", &self.role());
2385
2386 let supported_actions = action_mask_to_action_vec(self.actions);
2387 if !supported_actions.is_empty() {
2388 fmt.field("actions", &supported_actions);
2389 }
2390
2391 let child_supported_actions = action_mask_to_action_vec(self.child_actions);
2392 if !child_supported_actions.is_empty() {
2393 fmt.field("child_actions", &child_supported_actions);
2394 }
2395
2396 self.debug_flag_properties(&mut fmt);
2397 self.debug_node_id_vec_properties(&mut fmt);
2398 self.debug_node_id_properties(&mut fmt);
2399 self.debug_string_properties(&mut fmt);
2400 self.debug_f64_properties(&mut fmt);
2401 self.debug_f32_properties(&mut fmt);
2402 self.debug_usize_properties(&mut fmt);
2403 self.debug_color_properties(&mut fmt);
2404 self.debug_text_decoration_properties(&mut fmt);
2405 self.debug_length_slice_properties(&mut fmt);
2406 self.debug_coord_slice_properties(&mut fmt);
2407 self.debug_bool_properties(&mut fmt);
2408 self.debug_unique_enum_properties(&mut fmt);
2409 self.debug_option_properties(&mut fmt);
2410
2411 let custom_actions = self.custom_actions();
2412 if !custom_actions.is_empty() {
2413 fmt.field("custom_actions", &custom_actions);
2414 }
2415
2416 fmt.finish()
2417 }
2418}
2419
2420#[cfg(feature = "serde")]
2421macro_rules! serialize_property {
2422 ($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
2423 match &$self.values[$index as usize] {
2424 PropertyValue::None => (),
2425 $(PropertyValue::$variant(value) => {
2426 $map.serialize_entry(&$id, &value)?;
2427 })*
2428 }
2429 }
2430}
2431
2432#[cfg(feature = "serde")]
2433macro_rules! deserialize_property {
2434 ($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
2435 match $key {
2436 $($(PropertyId::$id => {
2437 let value = $map.next_value()?;
2438 $props.set(PropertyId::$id, PropertyValue::$type(value));
2439 })*)*
2440 PropertyId::Unset => {
2441 let _ = $map.next_value::<IgnoredAny>()?;
2442 }
2443 }
2444 }
2445}
2446
2447#[cfg(feature = "serde")]
2448impl Serialize for Properties {
2449 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2450 where
2451 S: Serializer,
2452 {
2453 let mut len = 0;
2454 for value in &*self.values {
2455 if !matches!(*value, PropertyValue::None) {
2456 len += 1;
2457 }
2458 }
2459 let mut map = serializer.serialize_map(Some(len))?;
2460 for (id, index) in self.indices.0.iter().copied().enumerate() {
2461 if index == PropertyId::Unset as u8 {
2462 continue;
2463 }
2464 let id = PropertyId::n(id as _).unwrap();
2465 serialize_property!(self, map, index, id, {
2466 NodeIdVec,
2467 NodeId,
2468 String,
2469 F64,
2470 F32,
2471 Usize,
2472 Color,
2473 TextDecoration,
2474 LengthSlice,
2475 CoordSlice,
2476 Bool,
2477 Invalid,
2478 Toggled,
2479 Live,
2480 TextDirection,
2481 Orientation,
2482 SortDirection,
2483 AriaCurrent,
2484 AutoComplete,
2485 HasPopup,
2486 ListStyle,
2487 TextAlign,
2488 VerticalOffset,
2489 Affine,
2490 Rect,
2491 TextSelection,
2492 CustomActionVec,
2493 TreeId
2494 });
2495 }
2496 map.end()
2497 }
2498}
2499
2500#[cfg(feature = "serde")]
2501struct PropertiesVisitor;
2502
2503#[cfg(feature = "serde")]
2504impl<'de> Visitor<'de> for PropertiesVisitor {
2505 type Value = Properties;
2506
2507 #[inline]
2508 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2509 formatter.write_str("property map")
2510 }
2511
2512 fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
2513 where
2514 V: MapAccess<'de>,
2515 {
2516 let mut props = Properties::default();
2517 while let Some(id) = map.next_key()? {
2518 deserialize_property!(props, map, id, {
2519 NodeIdVec {
2520 Children,
2521 Controls,
2522 Details,
2523 DescribedBy,
2524 FlowTo,
2525 LabelledBy,
2526 Owns,
2527 RadioGroup
2528 },
2529 NodeId {
2530 ActiveDescendant,
2531 ErrorMessage,
2532 InPageLinkTarget,
2533 MemberOf,
2534 NextOnLine,
2535 PreviousOnLine,
2536 PopupFor
2537 },
2538 String {
2539 Label,
2540 Description,
2541 Value,
2542 AccessKey,
2543 AuthorId,
2544 ClassName,
2545 FontFamily,
2546 HtmlTag,
2547 InnerHtml,
2548 KeyboardShortcut,
2549 Language,
2550 Placeholder,
2551 RoleDescription,
2552 StateDescription,
2553 Tooltip,
2554 Url,
2555 RowIndexText,
2556 ColumnIndexText,
2557 BrailleLabel,
2558 BrailleRoleDescription
2559 },
2560 F64 {
2561 ScrollX,
2562 ScrollXMin,
2563 ScrollXMax,
2564 ScrollY,
2565 ScrollYMin,
2566 ScrollYMax,
2567 NumericValue,
2568 MinNumericValue,
2569 MaxNumericValue,
2570 NumericValueStep,
2571 NumericValueJump
2572 },
2573 F32 {
2574 FontSize,
2575 FontWeight
2576 },
2577 Usize {
2578 RowCount,
2579 ColumnCount,
2580 RowIndex,
2581 ColumnIndex,
2582 RowSpan,
2583 ColumnSpan,
2584 Level,
2585 SizeOfSet,
2586 PositionInSet
2587 },
2588 Color {
2589 ColorValue,
2590 BackgroundColor,
2591 ForegroundColor
2592 },
2593 TextDecoration {
2594 Overline,
2595 Strikethrough,
2596 Underline
2597 },
2598 LengthSlice {
2599 CharacterLengths,
2600 WordStarts
2601 },
2602 CoordSlice {
2603 CharacterPositions,
2604 CharacterWidths
2605 },
2606 Bool {
2607 Expanded,
2608 Selected
2609 },
2610 Invalid { Invalid },
2611 Toggled { Toggled },
2612 Live { Live },
2613 TextDirection { TextDirection },
2614 Orientation { Orientation },
2615 SortDirection { SortDirection },
2616 AriaCurrent { AriaCurrent },
2617 AutoComplete { AutoComplete },
2618 HasPopup { HasPopup },
2619 ListStyle { ListStyle },
2620 TextAlign { TextAlign },
2621 VerticalOffset { VerticalOffset },
2622 Affine { Transform },
2623 Rect { Bounds },
2624 TextSelection { TextSelection },
2625 CustomActionVec { CustomActions },
2626 TreeId { TreeId }
2627 });
2628 }
2629
2630 Ok(props)
2631 }
2632}
2633
2634#[cfg(feature = "serde")]
2635impl<'de> Deserialize<'de> for Properties {
2636 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2637 where
2638 D: Deserializer<'de>,
2639 {
2640 deserializer.deserialize_map(PropertiesVisitor)
2641 }
2642}
2643
2644#[cfg(feature = "schemars")]
2645macro_rules! add_schema_property {
2646 ($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
2647 let name = format!("{:?}", $enum_value);
2648 let name = name[..1].to_ascii_lowercase() + &name[1..];
2649 let subschema = $gen.subschema_for::<$type>();
2650 $properties.insert(name, SchemaValue::from(subschema));
2651 }};
2652}
2653
2654#[cfg(feature = "schemars")]
2655macro_rules! add_properties_to_schema {
2656 ($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
2657 $($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
2658 }
2659}
2660
2661#[cfg(feature = "schemars")]
2662impl JsonSchema for Properties {
2663 #[inline]
2664 fn schema_name() -> Cow<'static, str> {
2665 "Properties".into()
2666 }
2667
2668 fn json_schema(generator: &mut SchemaGenerator) -> Schema {
2669 let mut properties = SchemaMap::<String, SchemaValue>::new();
2670 add_properties_to_schema!(generator, properties, {
2671 Vec<NodeId> {
2672 Children,
2673 Controls,
2674 Details,
2675 DescribedBy,
2676 FlowTo,
2677 LabelledBy,
2678 Owns,
2679 RadioGroup
2680 },
2681 NodeId {
2682 ActiveDescendant,
2683 ErrorMessage,
2684 InPageLinkTarget,
2685 MemberOf,
2686 NextOnLine,
2687 PreviousOnLine,
2688 PopupFor
2689 },
2690 Box<str> {
2691 Label,
2692 Description,
2693 Value,
2694 AccessKey,
2695 AuthorId,
2696 ClassName,
2697 FontFamily,
2698 HtmlTag,
2699 InnerHtml,
2700 KeyboardShortcut,
2701 Language,
2702 Placeholder,
2703 RoleDescription,
2704 StateDescription,
2705 Tooltip,
2706 Url,
2707 RowIndexText,
2708 ColumnIndexText,
2709 BrailleLabel,
2710 BrailleRoleDescription
2711 },
2712 f64 {
2713 ScrollX,
2714 ScrollXMin,
2715 ScrollXMax,
2716 ScrollY,
2717 ScrollYMin,
2718 ScrollYMax,
2719 NumericValue,
2720 MinNumericValue,
2721 MaxNumericValue,
2722 NumericValueStep,
2723 NumericValueJump
2724 },
2725 f32 {
2726 FontSize,
2727 FontWeight
2728 },
2729 usize {
2730 RowCount,
2731 ColumnCount,
2732 RowIndex,
2733 ColumnIndex,
2734 RowSpan,
2735 ColumnSpan,
2736 Level,
2737 SizeOfSet,
2738 PositionInSet
2739 },
2740 Color {
2741 ColorValue,
2742 BackgroundColor,
2743 ForegroundColor
2744 },
2745 TextDecoration {
2746 Overline,
2747 Strikethrough,
2748 Underline
2749 },
2750 Box<[u8]> {
2751 CharacterLengths,
2752 WordStarts
2753 },
2754 Box<[f32]> {
2755 CharacterPositions,
2756 CharacterWidths
2757 },
2758 bool {
2759 Expanded,
2760 Selected
2761 },
2762 Invalid { Invalid },
2763 Toggled { Toggled },
2764 Live { Live },
2765 TextDirection { TextDirection },
2766 Orientation { Orientation },
2767 SortDirection { SortDirection },
2768 AriaCurrent { AriaCurrent },
2769 AutoComplete { AutoComplete },
2770 HasPopup { HasPopup },
2771 ListStyle { ListStyle },
2772 TextAlign { TextAlign },
2773 VerticalOffset { VerticalOffset },
2774 Affine { Transform },
2775 Rect { Bounds },
2776 TextSelection { TextSelection },
2777 Vec<CustomAction> { CustomActions }
2778 });
2779 json_schema!({
2780 "type": "object",
2781 "properties": properties
2782 })
2783 }
2784}
2785
2786#[derive(Clone, Debug, PartialEq, Eq)]
2789#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2790#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2791#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2792#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2793pub struct Tree {
2794 pub root: NodeId,
2796 pub toolkit_name: Option<String>,
2798 pub toolkit_version: Option<String>,
2800}
2801
2802impl Tree {
2803 #[inline]
2804 pub fn new(root: NodeId) -> Tree {
2805 Tree {
2806 root,
2807 toolkit_name: None,
2808 toolkit_version: None,
2809 }
2810 }
2811}
2812
2813#[derive(Clone, Debug, PartialEq)]
2825#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2826#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2827#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2828#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2829pub struct TreeUpdate {
2830 pub nodes: Vec<(NodeId, Node)>,
2851
2852 pub tree: Option<Tree>,
2857
2858 pub tree_id: TreeId,
2868
2869 pub focus: NodeId,
2879}
2880
2881#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2884#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2885#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2886#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2887#[cfg_attr(
2888 feature = "pyo3",
2889 pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
2890)]
2891#[repr(u8)]
2892pub enum ScrollUnit {
2893 Item,
2897 Page,
2899}
2900
2901#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2904#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2905#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2906#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2907#[cfg_attr(
2908 feature = "pyo3",
2909 pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq)
2910)]
2911#[repr(u8)]
2912pub enum ScrollHint {
2913 TopLeft,
2914 BottomRight,
2915 TopEdge,
2916 BottomEdge,
2917 LeftEdge,
2918 RightEdge,
2919}
2920
2921#[derive(Clone, Debug, PartialEq)]
2922#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2923#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2924#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2925#[repr(C)]
2926pub enum ActionData {
2927 CustomAction(i32),
2928 Value(Box<str>),
2929 NumericValue(f64),
2930 ScrollUnit(ScrollUnit),
2931 ScrollHint(ScrollHint),
2935 ScrollToPoint(Point),
2938 SetScrollOffset(Point),
2941 SetTextSelection(TextSelection),
2942}
2943
2944#[derive(Clone, Debug, PartialEq)]
2945#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2946#[cfg_attr(feature = "schemars", derive(JsonSchema))]
2947#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
2948#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
2949pub struct ActionRequest {
2950 pub action: Action,
2951 pub target_tree: TreeId,
2952 pub target_node: NodeId,
2953 pub data: Option<ActionData>,
2954}
2955
2956pub trait ActivationHandler {
2958 fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
2980}
2981
2982pub trait ActionHandler {
2984 fn do_action(&mut self, request: ActionRequest);
2994}
2995
2996pub trait DeactivationHandler {
2998 fn deactivate_accessibility(&mut self);
3006}
3007
3008#[cfg(test)]
3009mod tests {
3010 use super::*;
3011 use alloc::format;
3012
3013 #[test]
3014 fn u64_should_be_convertible_to_node_id() {
3015 assert_eq!(NodeId::from(0u64), NodeId(0));
3016 assert_eq!(NodeId::from(1u64), NodeId(1));
3017 }
3018
3019 #[test]
3020 fn node_id_should_be_convertible_to_u64() {
3021 assert_eq!(u64::from(NodeId(0)), 0u64);
3022 assert_eq!(u64::from(NodeId(1)), 1u64);
3023 }
3024
3025 #[test]
3026 fn node_id_should_have_debug_repr() {
3027 assert_eq!(&format!("{:?}", NodeId(0)), "#0");
3028 assert_eq!(&format!("{:?}", NodeId(1)), "#1");
3029 }
3030
3031 #[test]
3032 fn action_n_should_return_the_corresponding_variant() {
3033 assert_eq!(Action::n(0), Some(Action::Click));
3034 assert_eq!(Action::n(1), Some(Action::Focus));
3035 assert_eq!(Action::n(2), Some(Action::Blur));
3036 assert_eq!(Action::n(3), Some(Action::Collapse));
3037 assert_eq!(Action::n(4), Some(Action::Expand));
3038 assert_eq!(Action::n(5), Some(Action::CustomAction));
3039 assert_eq!(Action::n(6), Some(Action::Decrement));
3040 assert_eq!(Action::n(7), Some(Action::Increment));
3041 assert_eq!(Action::n(8), Some(Action::HideTooltip));
3042 assert_eq!(Action::n(9), Some(Action::ShowTooltip));
3043 assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
3044 assert_eq!(Action::n(11), Some(Action::ScrollDown));
3045 assert_eq!(Action::n(12), Some(Action::ScrollLeft));
3046 assert_eq!(Action::n(13), Some(Action::ScrollRight));
3047 assert_eq!(Action::n(14), Some(Action::ScrollUp));
3048 assert_eq!(Action::n(15), Some(Action::ScrollIntoView));
3049 assert_eq!(Action::n(16), Some(Action::ScrollToPoint));
3050 assert_eq!(Action::n(17), Some(Action::SetScrollOffset));
3051 assert_eq!(Action::n(18), Some(Action::SetTextSelection));
3052 assert_eq!(
3053 Action::n(19),
3054 Some(Action::SetSequentialFocusNavigationStartingPoint)
3055 );
3056 assert_eq!(Action::n(20), Some(Action::SetValue));
3057 assert_eq!(Action::n(21), Some(Action::ShowContextMenu));
3058 assert_eq!(Action::n(22), None);
3059 }
3060
3061 #[test]
3062 fn empty_action_mask_should_be_converted_to_empty_vec() {
3063 assert_eq!(
3064 Vec::<Action>::new(),
3065 action_mask_to_action_vec(Node::new(Role::Unknown).actions)
3066 );
3067 }
3068
3069 #[test]
3070 fn action_mask_should_be_convertible_to_vec() {
3071 let mut node = Node::new(Role::Unknown);
3072 node.add_action(Action::Click);
3073 assert_eq!(
3074 &[Action::Click],
3075 action_mask_to_action_vec(node.actions).as_slice()
3076 );
3077
3078 let mut node = Node::new(Role::Unknown);
3079 node.add_action(Action::ShowContextMenu);
3080 assert_eq!(
3081 &[Action::ShowContextMenu],
3082 action_mask_to_action_vec(node.actions).as_slice()
3083 );
3084
3085 let mut node = Node::new(Role::Unknown);
3086 node.add_action(Action::Click);
3087 node.add_action(Action::ShowContextMenu);
3088 assert_eq!(
3089 &[Action::Click, Action::ShowContextMenu],
3090 action_mask_to_action_vec(node.actions).as_slice()
3091 );
3092
3093 let mut node = Node::new(Role::Unknown);
3094 node.add_action(Action::Focus);
3095 node.add_action(Action::Blur);
3096 node.add_action(Action::Collapse);
3097 assert_eq!(
3098 &[Action::Focus, Action::Blur, Action::Collapse],
3099 action_mask_to_action_vec(node.actions).as_slice()
3100 );
3101 }
3102
3103 #[test]
3104 fn new_node_should_have_user_provided_role() {
3105 let node = Node::new(Role::Button);
3106 assert_eq!(node.role(), Role::Button);
3107 }
3108
3109 #[test]
3110 fn node_role_setter_should_update_the_role() {
3111 let mut node = Node::new(Role::Button);
3112 node.set_role(Role::CheckBox);
3113 assert_eq!(node.role(), Role::CheckBox);
3114 }
3115
3116 macro_rules! assert_absent_action {
3117 ($node:ident, $action:ident) => {
3118 assert!(!$node.supports_action(Action::$action));
3119 assert!(!$node.child_supports_action(Action::$action));
3120 };
3121 }
3122
3123 #[test]
3124 fn new_node_should_not_support_anyaction() {
3125 let node = Node::new(Role::Unknown);
3126 assert_absent_action!(node, Click);
3127 assert_absent_action!(node, Focus);
3128 assert_absent_action!(node, Blur);
3129 assert_absent_action!(node, Collapse);
3130 assert_absent_action!(node, Expand);
3131 assert_absent_action!(node, CustomAction);
3132 assert_absent_action!(node, Decrement);
3133 assert_absent_action!(node, Increment);
3134 assert_absent_action!(node, HideTooltip);
3135 assert_absent_action!(node, ShowTooltip);
3136 assert_absent_action!(node, ReplaceSelectedText);
3137 assert_absent_action!(node, ScrollDown);
3138 assert_absent_action!(node, ScrollLeft);
3139 assert_absent_action!(node, ScrollRight);
3140 assert_absent_action!(node, ScrollUp);
3141 assert_absent_action!(node, ScrollIntoView);
3142 assert_absent_action!(node, ScrollToPoint);
3143 assert_absent_action!(node, SetScrollOffset);
3144 assert_absent_action!(node, SetTextSelection);
3145 assert_absent_action!(node, SetSequentialFocusNavigationStartingPoint);
3146 assert_absent_action!(node, SetValue);
3147 assert_absent_action!(node, ShowContextMenu);
3148 }
3149
3150 #[test]
3151 fn node_add_action_should_add_the_action() {
3152 let mut node = Node::new(Role::Unknown);
3153 node.add_action(Action::Focus);
3154 assert!(node.supports_action(Action::Focus));
3155 node.add_action(Action::Blur);
3156 assert!(node.supports_action(Action::Blur));
3157 }
3158
3159 #[test]
3160 fn node_add_child_action_should_add_the_action() {
3161 let mut node = Node::new(Role::Unknown);
3162 node.add_child_action(Action::Focus);
3163 assert!(node.child_supports_action(Action::Focus));
3164 node.add_child_action(Action::Blur);
3165 assert!(node.child_supports_action(Action::Blur));
3166 }
3167
3168 #[test]
3169 fn node_add_action_should_do_nothing_if_the_action_is_already_supported() {
3170 let mut node = Node::new(Role::Unknown);
3171 node.add_action(Action::Focus);
3172 node.add_action(Action::Focus);
3173 assert!(node.supports_action(Action::Focus));
3174 }
3175
3176 #[test]
3177 fn node_add_child_action_should_do_nothing_if_the_action_is_already_supported() {
3178 let mut node = Node::new(Role::Unknown);
3179 node.add_child_action(Action::Focus);
3180 node.add_child_action(Action::Focus);
3181 assert!(node.child_supports_action(Action::Focus));
3182 }
3183
3184 #[test]
3185 fn node_remove_action_should_remove_the_action() {
3186 let mut node = Node::new(Role::Unknown);
3187 node.add_action(Action::Blur);
3188 node.remove_action(Action::Blur);
3189 assert!(!node.supports_action(Action::Blur));
3190 }
3191
3192 #[test]
3193 fn node_remove_child_action_should_remove_the_action() {
3194 let mut node = Node::new(Role::Unknown);
3195 node.add_child_action(Action::Blur);
3196 node.remove_child_action(Action::Blur);
3197 assert!(!node.child_supports_action(Action::Blur));
3198 }
3199
3200 #[test]
3201 fn node_clear_actions_should_remove_all_actions() {
3202 let mut node = Node::new(Role::Unknown);
3203 node.add_action(Action::Focus);
3204 node.add_action(Action::Blur);
3205 node.clear_actions();
3206 assert!(!node.supports_action(Action::Focus));
3207 assert!(!node.supports_action(Action::Blur));
3208 }
3209
3210 #[test]
3211 fn node_clear_child_actions_should_remove_all_actions() {
3212 let mut node = Node::new(Role::Unknown);
3213 node.add_child_action(Action::Focus);
3214 node.add_child_action(Action::Blur);
3215 node.clear_child_actions();
3216 assert!(!node.child_supports_action(Action::Focus));
3217 assert!(!node.child_supports_action(Action::Blur));
3218 }
3219
3220 #[test]
3221 fn node_should_have_debug_repr() {
3222 let mut node = Node::new(Role::Unknown);
3223 node.add_action(Action::Click);
3224 node.add_action(Action::Focus);
3225 node.add_child_action(Action::ScrollIntoView);
3226 node.set_hidden();
3227 node.set_multiselectable();
3228 node.set_children([NodeId(0), NodeId(1)]);
3229 node.set_active_descendant(NodeId(2));
3230 node.push_custom_action(CustomAction {
3231 id: 0,
3232 description: "test action".into(),
3233 });
3234
3235 assert_eq!(
3236 &format!("{node:?}"),
3237 r#"Node { role: Unknown, actions: [Click, Focus], child_actions: [ScrollIntoView], is_hidden: true, is_multiselectable: true, children: [#0, #1], active_descendant: #2, custom_actions: [CustomAction { id: 0, description: "test action" }] }"#
3238 );
3239 }
3240
3241 #[test]
3242 fn new_tree_should_have_root_id() {
3243 let tree = Tree::new(NodeId(1));
3244 assert_eq!(tree.root, NodeId(1));
3245 assert_eq!(tree.toolkit_name, None);
3246 assert_eq!(tree.toolkit_version, None);
3247 }
3248}