#![cfg_attr(not(any(feature = "pyo3", feature = "schemars")), no_std)]
extern crate alloc;
use alloc::{boxed::Box, string::String, vec::Vec};
use core::fmt;
#[cfg(feature = "pyo3")]
use pyo3::pyclass;
#[cfg(feature = "schemars")]
use schemars::{
gen::SchemaGenerator,
schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
JsonSchema, Map as SchemaMap,
};
#[cfg(feature = "serde")]
use serde::{
de::{Deserializer, IgnoredAny, MapAccess, Visitor},
ser::{SerializeMap, Serializer},
Deserialize, Serialize,
};
mod geometry;
pub use geometry::{Affine, Point, Rect, Size, Vec2};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum Role {
#[default]
Unknown,
TextRun,
Cell,
Label,
Image,
Link,
Row,
ListItem,
ListMarker,
TreeItem,
ListBoxOption,
MenuItem,
MenuListOption,
Paragraph,
GenericContainer,
CheckBox,
RadioButton,
TextInput,
Button,
DefaultButton,
Pane,
RowHeader,
ColumnHeader,
RowGroup,
List,
Table,
LayoutTableCell,
LayoutTableRow,
LayoutTable,
Switch,
Menu,
MultilineTextInput,
SearchInput,
DateInput,
DateTimeInput,
WeekInput,
MonthInput,
TimeInput,
EmailInput,
NumberInput,
PasswordInput,
PhoneNumberInput,
UrlInput,
Abbr,
Alert,
AlertDialog,
Application,
Article,
Audio,
Banner,
Blockquote,
Canvas,
Caption,
Caret,
Code,
ColorWell,
ComboBox,
EditableComboBox,
Complementary,
Comment,
ContentDeletion,
ContentInsertion,
ContentInfo,
Definition,
DescriptionList,
DescriptionListDetail,
DescriptionListTerm,
Details,
Dialog,
Directory,
DisclosureTriangle,
Document,
EmbeddedObject,
Emphasis,
Feed,
FigureCaption,
Figure,
Footer,
FooterAsNonLandmark,
Form,
Grid,
Group,
Header,
HeaderAsNonLandmark,
Heading,
Iframe,
IframePresentational,
ImeCandidate,
Keyboard,
Legend,
LineBreak,
ListBox,
Log,
Main,
Mark,
Marquee,
Math,
MenuBar,
MenuItemCheckBox,
MenuItemRadio,
MenuListPopup,
Meter,
Navigation,
Note,
PluginObject,
Portal,
Pre,
ProgressIndicator,
RadioGroup,
Region,
RootWebArea,
Ruby,
RubyAnnotation,
ScrollBar,
ScrollView,
Search,
Section,
Slider,
SpinButton,
Splitter,
Status,
Strong,
Suggestion,
SvgRoot,
Tab,
TabList,
TabPanel,
Term,
Time,
Timer,
TitleBar,
Toolbar,
Tooltip,
Tree,
TreeGrid,
Video,
WebView,
Window,
PdfActionableHighlight,
PdfRoot,
GraphicsDocument,
GraphicsObject,
GraphicsSymbol,
DocAbstract,
DocAcknowledgements,
DocAfterword,
DocAppendix,
DocBackLink,
DocBiblioEntry,
DocBibliography,
DocBiblioRef,
DocChapter,
DocColophon,
DocConclusion,
DocCover,
DocCredit,
DocCredits,
DocDedication,
DocEndnote,
DocEndnotes,
DocEpigraph,
DocEpilogue,
DocErrata,
DocExample,
DocFootnote,
DocForeword,
DocGlossary,
DocGlossRef,
DocIndex,
DocIntroduction,
DocNoteRef,
DocNotice,
DocPageBreak,
DocPageFooter,
DocPageHeader,
DocPageList,
DocPart,
DocPreface,
DocPrologue,
DocPullquote,
DocQna,
DocSubtitle,
DocTip,
DocToc,
ListGrid,
Terminal,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum Action {
Click,
Focus,
Blur,
Collapse,
Expand,
CustomAction,
Decrement,
Increment,
HideTooltip,
ShowTooltip,
ReplaceSelectedText,
ScrollBackward,
ScrollDown,
ScrollForward,
ScrollLeft,
ScrollRight,
ScrollUp,
ScrollIntoView,
ScrollToPoint,
SetScrollOffset,
SetTextSelection,
SetSequentialFocusNavigationStartingPoint,
SetValue,
ShowContextMenu,
}
impl Action {
fn mask(self) -> u32 {
1 << (self as u8)
}
#[cfg(not(feature = "enumn"))]
fn n(value: u8) -> Option<Self> {
match value {
0 => Some(Action::Click),
1 => Some(Action::Focus),
2 => Some(Action::Blur),
3 => Some(Action::Collapse),
4 => Some(Action::Expand),
5 => Some(Action::CustomAction),
6 => Some(Action::Decrement),
7 => Some(Action::Increment),
8 => Some(Action::HideTooltip),
9 => Some(Action::ShowTooltip),
10 => Some(Action::ReplaceSelectedText),
11 => Some(Action::ScrollBackward),
12 => Some(Action::ScrollDown),
13 => Some(Action::ScrollForward),
14 => Some(Action::ScrollLeft),
15 => Some(Action::ScrollRight),
16 => Some(Action::ScrollUp),
17 => Some(Action::ScrollIntoView),
18 => Some(Action::ScrollToPoint),
19 => Some(Action::SetScrollOffset),
20 => Some(Action::SetTextSelection),
21 => Some(Action::SetSequentialFocusNavigationStartingPoint),
22 => Some(Action::SetValue),
23 => Some(Action::ShowContextMenu),
_ => None,
}
}
}
fn action_mask_to_action_vec(mask: u32) -> Vec<Action> {
let mut actions = Vec::new();
let mut i = 0;
while let Some(variant) = Action::n(i) {
if mask & variant.mask() != 0 {
actions.push(variant);
}
i += 1;
}
actions
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum Orientation {
Horizontal,
Vertical,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum TextDirection {
LeftToRight,
RightToLeft,
TopToBottom,
BottomToTop,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum Invalid {
True,
Grammar,
Spelling,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum Toggled {
False,
True,
Mixed,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum SortDirection {
Ascending,
Descending,
Other,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum AriaCurrent {
False,
True,
Page,
Step,
Location,
Date,
Time,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum AutoComplete {
Inline,
List,
Both,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum Live {
Off,
Polite,
Assertive,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum HasPopup {
True,
Menu,
Listbox,
Tree,
Grid,
Dialog,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum ListStyle {
Circle,
Disc,
Image,
Numeric,
Square,
Other,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum TextAlign {
Left,
Right,
Center,
Justify,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum VerticalOffset {
Subscript,
Superscript,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "enumn", derive(enumn::N))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(
feature = "pyo3",
pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")
)]
#[repr(u8)]
pub enum TextDecoration {
Solid,
Dotted,
Dashed,
Double,
Wavy,
}
pub type NodeIdContent = u64;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[repr(transparent)]
pub struct NodeId(pub NodeIdContent);
impl From<NodeIdContent> for NodeId {
#[inline]
fn from(inner: NodeIdContent) -> Self {
Self(inner)
}
}
impl From<NodeId> for NodeIdContent {
#[inline]
fn from(outer: NodeId) -> Self {
outer.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct CustomAction {
pub id: i32,
pub description: Box<str>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct TextPosition {
pub node: NodeId,
pub character_index: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct TextSelection {
pub anchor: TextPosition,
pub focus: TextPosition,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[repr(u8)]
enum Flag {
Hidden,
Linked,
Multiselectable,
Required,
Visited,
Busy,
LiveAtomic,
Modal,
TouchTransparent,
ReadOnly,
Disabled,
Bold,
Italic,
ClipsChildren,
IsLineBreakingObject,
IsPageBreakingObject,
IsSpellingError,
IsGrammarError,
IsSearchMatch,
IsSuggestion,
}
impl Flag {
fn mask(self) -> u32 {
1 << (self as u8)
}
}
#[derive(Clone, Debug, PartialEq)]
enum PropertyValue {
None,
NodeIdVec(Vec<NodeId>),
NodeId(NodeId),
String(Box<str>),
F64(f64),
Usize(usize),
Color(u32),
TextDecoration(TextDecoration),
LengthSlice(Box<[u8]>),
CoordSlice(Box<[f32]>),
Bool(bool),
Invalid(Invalid),
Toggled(Toggled),
Live(Live),
TextDirection(TextDirection),
Orientation(Orientation),
SortDirection(SortDirection),
AriaCurrent(AriaCurrent),
AutoComplete(AutoComplete),
HasPopup(HasPopup),
ListStyle(ListStyle),
TextAlign(TextAlign),
VerticalOffset(VerticalOffset),
Affine(Box<Affine>),
Rect(Rect),
TextSelection(Box<TextSelection>),
CustomActionVec(Vec<CustomAction>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize, enumn::N))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[repr(u8)]
enum PropertyId {
Children,
Controls,
Details,
DescribedBy,
FlowTo,
LabelledBy,
Owns,
RadioGroup,
ActiveDescendant,
ErrorMessage,
InPageLinkTarget,
MemberOf,
NextOnLine,
PreviousOnLine,
PopupFor,
Label,
Description,
Value,
AccessKey,
AuthorId,
ClassName,
FontFamily,
HtmlTag,
InnerHtml,
KeyboardShortcut,
Language,
Placeholder,
RoleDescription,
StateDescription,
Tooltip,
Url,
RowIndexText,
ColumnIndexText,
ScrollX,
ScrollXMin,
ScrollXMax,
ScrollY,
ScrollYMin,
ScrollYMax,
NumericValue,
MinNumericValue,
MaxNumericValue,
NumericValueStep,
NumericValueJump,
FontSize,
FontWeight,
RowCount,
ColumnCount,
RowIndex,
ColumnIndex,
RowSpan,
ColumnSpan,
Level,
SizeOfSet,
PositionInSet,
ColorValue,
BackgroundColor,
ForegroundColor,
Overline,
Strikethrough,
Underline,
CharacterLengths,
WordLengths,
CharacterPositions,
CharacterWidths,
Expanded,
Selected,
Invalid,
Toggled,
Live,
TextDirection,
Orientation,
SortDirection,
AriaCurrent,
AutoComplete,
HasPopup,
ListStyle,
TextAlign,
VerticalOffset,
Transform,
Bounds,
TextSelection,
CustomActions,
Unset,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
struct PropertyIndices([u8; PropertyId::Unset as usize]);
impl Default for PropertyIndices {
fn default() -> Self {
Self([PropertyId::Unset as u8; PropertyId::Unset as usize])
}
}
#[derive(Clone, Debug, PartialEq)]
struct FrozenProperties {
indices: PropertyIndices,
values: Box<[PropertyValue]>,
}
#[derive(Clone, PartialEq)]
pub struct FrozenNode {
role: Role,
actions: u32,
flags: u32,
properties: FrozenProperties,
}
#[derive(Clone, Debug, Default, PartialEq)]
struct Properties {
indices: PropertyIndices,
values: Vec<PropertyValue>,
}
#[derive(Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Node {
role: Role,
actions: u32,
flags: u32,
properties: Properties,
}
impl PropertyIndices {
fn get<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a PropertyValue {
let index = self.0[id as usize];
if index == PropertyId::Unset as u8 {
&PropertyValue::None
} else {
&values[index as usize]
}
}
}
fn unexpected_property_type() -> ! {
panic!();
}
impl Properties {
fn get_mut(&mut self, id: PropertyId, default: PropertyValue) -> &mut PropertyValue {
let index = self.indices.0[id as usize] as usize;
if index == PropertyId::Unset as usize {
self.values.push(default);
let index = self.values.len() - 1;
self.indices.0[id as usize] = index as u8;
&mut self.values[index]
} else {
if matches!(self.values[index], PropertyValue::None) {
self.values[index] = default;
}
&mut self.values[index]
}
}
fn set(&mut self, id: PropertyId, value: PropertyValue) {
let index = self.indices.0[id as usize];
if index == PropertyId::Unset as u8 {
self.values.push(value);
self.indices.0[id as usize] = (self.values.len() - 1) as u8;
} else {
self.values[index as usize] = value;
}
}
fn clear(&mut self, id: PropertyId) {
let index = self.indices.0[id as usize];
if index != PropertyId::Unset as u8 {
self.values[index as usize] = PropertyValue::None;
}
}
}
impl From<Properties> for FrozenProperties {
fn from(props: Properties) -> Self {
Self {
indices: props.indices,
values: props.values.into_boxed_slice(),
}
}
}
macro_rules! flag_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
impl FrozenNode {
$($(#[$doc])*
#[inline]
pub fn $getter(&self) -> bool {
(self.flags & (Flag::$id).mask()) != 0
})*
fn debug_flag_properties(&self, fmt: &mut fmt::DebugStruct) {
$(
if self.$getter() {
fmt.field(stringify!($getter), &true);
}
)*
}
}
impl Node {
$($(#[$doc])*
#[inline]
pub fn $getter(&self) -> bool {
(self.flags & (Flag::$id).mask()) != 0
}
#[inline]
pub fn $setter(&mut self) {
self.flags |= (Flag::$id).mask();
}
#[inline]
pub fn $clearer(&mut self) {
self.flags &= !((Flag::$id).mask());
})*
fn debug_flag_properties(&self, fmt: &mut fmt::DebugStruct) {
$(
if self.$getter() {
fmt.field(stringify!($getter), &true);
}
)*
}
}
}
}
macro_rules! option_ref_type_getters {
($(($method:ident, $type:ty, $variant:ident)),+) => {
impl PropertyIndices {
$(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> Option<&'a $type> {
match self.get(values, id) {
PropertyValue::None => None,
PropertyValue::$variant(value) => Some(value),
_ => unexpected_property_type(),
}
})*
}
}
}
macro_rules! slice_type_getters {
($(($method:ident, $type:ty, $variant:ident)),+) => {
impl PropertyIndices {
$(fn $method<'a>(&self, values: &'a [PropertyValue], id: PropertyId) -> &'a [$type] {
match self.get(values, id) {
PropertyValue::None => &[],
PropertyValue::$variant(value) => value,
_ => unexpected_property_type(),
}
})*
}
}
}
macro_rules! copy_type_getters {
($(($method:ident, $type:ty, $variant:ident)),+) => {
impl PropertyIndices {
$(fn $method(&self, values: &[PropertyValue], id: PropertyId) -> Option<$type> {
match self.get(values, id) {
PropertyValue::None => None,
PropertyValue::$variant(value) => Some(*value),
_ => unexpected_property_type(),
}
})*
}
}
}
macro_rules! box_type_setters {
($(($method:ident, $type:ty, $variant:ident)),+) => {
impl Node {
$(fn $method(&mut self, id: PropertyId, value: impl Into<Box<$type>>) {
self.properties.set(id, PropertyValue::$variant(value.into()));
})*
}
}
}
macro_rules! copy_type_setters {
($(($method:ident, $type:ty, $variant:ident)),+) => {
impl Node {
$(fn $method(&mut self, id: PropertyId, value: $type) {
self.properties.set(id, PropertyValue::$variant(value));
})*
}
}
}
macro_rules! vec_type_methods {
($(($type:ty, $variant:ident, $getter:ident, $setter:ident, $pusher:ident)),+) => {
$(slice_type_getters! {
($getter, $type, $variant)
})*
impl Node {
$(fn $setter(&mut self, id: PropertyId, value: impl Into<Vec<$type>>) {
self.properties.set(id, PropertyValue::$variant(value.into()));
}
fn $pusher(&mut self, id: PropertyId, item: $type) {
match self.properties.get_mut(id, PropertyValue::$variant(Vec::new())) {
PropertyValue::$variant(v) => {
v.push(item);
}
_ => unexpected_property_type(),
}
})*
}
}
}
macro_rules! property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $type_getter:ident, $getter_result:ty, $setter:ident, $type_setter:ident, $setter_param:ty, $clearer:ident)),+) => {
impl FrozenNode {
$($(#[$doc])*
#[inline]
pub fn $getter(&self) -> $getter_result {
self.properties.indices.$type_getter(&self.properties.values, PropertyId::$id)
})*
}
impl Node {
$($(#[$doc])*
#[inline]
pub fn $getter(&self) -> $getter_result {
self.properties.indices.$type_getter(&self.properties.values, PropertyId::$id)
}
#[inline]
pub fn $setter(&mut self, value: $setter_param) {
self.$type_setter(PropertyId::$id, value);
}
#[inline]
pub fn $clearer(&mut self) {
self.properties.clear(PropertyId::$id);
})*
}
}
}
macro_rules! vec_property_methods {
($($(#[$doc:meta])* ($id:ident, $item_type:ty, $getter:ident, $type_getter:ident, $setter:ident, $type_setter:ident, $pusher:ident, $type_pusher:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, $type_getter, &[$item_type], $setter, $type_setter, impl Into<Vec<$item_type>>, $clearer)
}
impl Node {
#[inline]
pub fn $pusher(&mut self, item: $item_type) {
self.$type_pusher(PropertyId::$id, item);
}
})*
}
}
macro_rules! slice_properties_debug_method {
($name:ident, [$($getter:ident,)*]) => {
fn $name(&self, fmt: &mut fmt::DebugStruct) {
$(
let value = self.$getter();
if !value.is_empty() {
fmt.field(stringify!($getter), &value);
}
)*
}
}
}
macro_rules! node_id_vec_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $pusher:ident, $clearer:ident)),+) => {
$(vec_property_methods! {
$(#[$doc])*
($id, NodeId, $getter, get_node_id_vec, $setter, set_node_id_vec, $pusher, push_to_node_id_vec, $clearer)
})*
impl FrozenNode {
slice_properties_debug_method! { debug_node_id_vec_properties, [$($getter,)*] }
}
impl Node {
slice_properties_debug_method! { debug_node_id_vec_properties, [$($getter,)*] }
}
}
}
macro_rules! option_properties_debug_method {
($name:ident, [$($getter:ident,)*]) => {
fn $name(&self, fmt: &mut fmt::DebugStruct) {
$(
if let Some(value) = self.$getter() {
fmt.field(stringify!($getter), &value);
}
)*
}
}
}
macro_rules! node_id_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_node_id_property, Option<NodeId>, $setter, set_node_id_property, NodeId, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_node_id_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_node_id_properties, [$($getter,)*] }
}
}
}
macro_rules! string_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_string_property, Option<&str>, $setter, set_string_property, impl Into<Box<str>>, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_string_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_string_properties, [$($getter,)*] }
}
}
}
macro_rules! f64_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_f64_property, Option<f64>, $setter, set_f64_property, f64, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_f64_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_f64_properties, [$($getter,)*] }
}
}
}
macro_rules! usize_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_usize_property, Option<usize>, $setter, set_usize_property, usize, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_usize_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_usize_properties, [$($getter,)*] }
}
}
}
macro_rules! color_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_color_property, Option<u32>, $setter, set_color_property, u32, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_color_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_color_properties, [$($getter,)*] }
}
}
}
macro_rules! text_decoration_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_text_decoration_property, Option<TextDecoration>, $setter, set_text_decoration_property, TextDecoration, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_text_decoration_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_text_decoration_properties, [$($getter,)*] }
}
}
}
macro_rules! length_slice_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_length_slice_property, &[u8], $setter, set_length_slice_property, impl Into<Box<[u8]>>, $clearer)
})*
impl FrozenNode {
slice_properties_debug_method! { debug_length_slice_properties, [$($getter,)*] }
}
impl Node {
slice_properties_debug_method! { debug_length_slice_properties, [$($getter,)*] }
}
}
}
macro_rules! coord_slice_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_coord_slice_property, Option<&[f32]>, $setter, set_coord_slice_property, impl Into<Box<[f32]>>, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_coord_slice_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_coord_slice_properties, [$($getter,)*] }
}
}
}
macro_rules! bool_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
$(property_methods! {
$(#[$doc])*
($id, $getter, get_bool_property, Option<bool>, $setter, set_bool_property, bool, $clearer)
})*
impl FrozenNode {
option_properties_debug_method! { debug_bool_properties, [$($getter,)*] }
}
impl Node {
option_properties_debug_method! { debug_bool_properties, [$($getter,)*] }
}
}
}
macro_rules! unique_enum_property_methods {
($($(#[$doc:meta])* ($id:ident, $getter:ident, $setter:ident, $clearer:ident)),+) => {
impl FrozenNode {
$($(#[$doc])*
#[inline]
pub fn $getter(&self) -> Option<$id> {
match self.properties.indices.get(&self.properties.values, PropertyId::$id) {
PropertyValue::None => None,
PropertyValue::$id(value) => Some(*value),
_ => unexpected_property_type(),
}
})*
option_properties_debug_method! { debug_unique_enum_properties, [$($getter,)*] }
}
impl Node {
$($(#[$doc])*
#[inline]
pub fn $getter(&self) -> Option<$id> {
match self.properties.indices.get(&self.properties.values, PropertyId::$id) {
PropertyValue::None => None,
PropertyValue::$id(value) => Some(*value),
_ => unexpected_property_type(),
}
}
#[inline]
pub fn $setter(&mut self, value: $id) {
self.properties.set(PropertyId::$id, PropertyValue::$id(value));
}
#[inline]
pub fn $clearer(&mut self) {
self.properties.clear(PropertyId::$id);
})*
option_properties_debug_method! { debug_unique_enum_properties, [$($getter,)*] }
}
}
}
impl Node {
#[inline]
pub fn new(role: Role) -> Self {
Self {
role,
..Default::default()
}
}
}
impl From<Node> for FrozenNode {
fn from(node: Node) -> Self {
Self {
role: node.role,
actions: node.actions,
flags: node.flags,
properties: node.properties.into(),
}
}
}
impl FrozenNode {
#[inline]
pub fn role(&self) -> Role {
self.role
}
}
impl Node {
#[inline]
pub fn role(&self) -> Role {
self.role
}
#[inline]
pub fn set_role(&mut self, value: Role) {
self.role = value;
}
}
impl FrozenNode {
#[inline]
pub fn supports_action(&self, action: Action) -> bool {
(self.actions & action.mask()) != 0
}
}
impl Node {
#[inline]
pub fn supports_action(&self, action: Action) -> bool {
(self.actions & action.mask()) != 0
}
#[inline]
pub fn add_action(&mut self, action: Action) {
self.actions |= action.mask();
}
#[inline]
pub fn remove_action(&mut self, action: Action) {
self.actions &= !(action.mask());
}
#[inline]
pub fn clear_actions(&mut self) {
self.actions = 0;
}
}
flag_methods! {
(Hidden, is_hidden, set_hidden, clear_hidden),
(Linked, is_linked, set_linked, clear_linked),
(Multiselectable, is_multiselectable, set_multiselectable, clear_multiselectable),
(Required, is_required, set_required, clear_required),
(Visited, is_visited, set_visited, clear_visited),
(Busy, is_busy, set_busy, clear_busy),
(LiveAtomic, is_live_atomic, set_live_atomic, clear_live_atomic),
(Modal, is_modal, set_modal, clear_modal),
(TouchTransparent, is_touch_transparent, set_touch_transparent, clear_touch_transparent),
(ReadOnly, is_read_only, set_read_only, clear_read_only),
(Disabled, is_disabled, set_disabled, clear_disabled),
(Bold, is_bold, set_bold, clear_bold),
(Italic, is_italic, set_italic, clear_italic),
(ClipsChildren, clips_children, set_clips_children, clear_clips_children),
(IsLineBreakingObject, is_line_breaking_object, set_is_line_breaking_object, clear_is_line_breaking_object),
(IsPageBreakingObject, is_page_breaking_object, set_is_page_breaking_object, clear_is_page_breaking_object),
(IsSpellingError, is_spelling_error, set_is_spelling_error, clear_is_spelling_error),
(IsGrammarError, is_grammar_error, set_is_grammar_error, clear_is_grammar_error),
(IsSearchMatch, is_search_match, set_is_search_match, clear_is_search_match),
(IsSuggestion, is_suggestion, set_is_suggestion, clear_is_suggestion)
}
option_ref_type_getters! {
(get_affine_property, Affine, Affine),
(get_string_property, str, String),
(get_coord_slice_property, [f32], CoordSlice),
(get_text_selection_property, TextSelection, TextSelection)
}
slice_type_getters! {
(get_length_slice_property, u8, LengthSlice)
}
copy_type_getters! {
(get_rect_property, Rect, Rect),
(get_node_id_property, NodeId, NodeId),
(get_f64_property, f64, F64),
(get_usize_property, usize, Usize),
(get_color_property, u32, Color),
(get_text_decoration_property, TextDecoration, TextDecoration),
(get_bool_property, bool, Bool)
}
box_type_setters! {
(set_affine_property, Affine, Affine),
(set_string_property, str, String),
(set_length_slice_property, [u8], LengthSlice),
(set_coord_slice_property, [f32], CoordSlice),
(set_text_selection_property, TextSelection, TextSelection)
}
copy_type_setters! {
(set_rect_property, Rect, Rect),
(set_node_id_property, NodeId, NodeId),
(set_f64_property, f64, F64),
(set_usize_property, usize, Usize),
(set_color_property, u32, Color),
(set_text_decoration_property, TextDecoration, TextDecoration),
(set_bool_property, bool, Bool)
}
vec_type_methods! {
(NodeId, NodeIdVec, get_node_id_vec, set_node_id_vec, push_to_node_id_vec),
(CustomAction, CustomActionVec, get_custom_action_vec, set_custom_action_vec, push_to_custom_action_vec)
}
node_id_vec_property_methods! {
(Children, children, set_children, push_child, clear_children),
(Controls, controls, set_controls, push_controlled, clear_controls),
(Details, details, set_details, push_detail, clear_details),
(DescribedBy, described_by, set_described_by, push_described_by, clear_described_by),
(FlowTo, flow_to, set_flow_to, push_flow_to, clear_flow_to),
(LabelledBy, labelled_by, set_labelled_by, push_labelled_by, clear_labelled_by),
(Owns, owns, set_owns, push_owned, clear_owns),
(RadioGroup, radio_group, set_radio_group, push_to_radio_group, clear_radio_group)
}
node_id_property_methods! {
(ActiveDescendant, active_descendant, set_active_descendant, clear_active_descendant),
(ErrorMessage, error_message, set_error_message, clear_error_message),
(InPageLinkTarget, in_page_link_target, set_in_page_link_target, clear_in_page_link_target),
(MemberOf, member_of, set_member_of, clear_member_of),
(NextOnLine, next_on_line, set_next_on_line, clear_next_on_line),
(PreviousOnLine, previous_on_line, set_previous_on_line, clear_previous_on_line),
(PopupFor, popup_for, set_popup_for, clear_popup_for)
}
string_property_methods! {
(Label, label, set_label, clear_label),
(Description, description, set_description, clear_description),
(Value, value, set_value, clear_value),
(AccessKey, access_key, set_access_key, clear_access_key),
(AuthorId, author_id, set_author_id, clear_author_id),
(ClassName, class_name, set_class_name, clear_class_name),
(FontFamily, font_family, set_font_family, clear_font_family),
(HtmlTag, html_tag, set_html_tag, clear_html_tag),
(InnerHtml, inner_html, set_inner_html, clear_inner_html),
(KeyboardShortcut, keyboard_shortcut, set_keyboard_shortcut, clear_keyboard_shortcut),
(Language, language, set_language, clear_language),
(Placeholder, placeholder, set_placeholder, clear_placeholder),
(RoleDescription, role_description, set_role_description, clear_role_description),
(StateDescription, state_description, set_state_description, clear_state_description),
(Tooltip, tooltip, set_tooltip, clear_tooltip),
(Url, url, set_url, clear_url),
(RowIndexText, row_index_text, set_row_index_text, clear_row_index_text),
(ColumnIndexText, column_index_text, set_column_index_text, clear_column_index_text)
}
f64_property_methods! {
(ScrollX, scroll_x, set_scroll_x, clear_scroll_x),
(ScrollXMin, scroll_x_min, set_scroll_x_min, clear_scroll_x_min),
(ScrollXMax, scroll_x_max, set_scroll_x_max, clear_scroll_x_max),
(ScrollY, scroll_y, set_scroll_y, clear_scroll_y),
(ScrollYMin, scroll_y_min, set_scroll_y_min, clear_scroll_y_min),
(ScrollYMax, scroll_y_max, set_scroll_y_max, clear_scroll_y_max),
(NumericValue, numeric_value, set_numeric_value, clear_numeric_value),
(MinNumericValue, min_numeric_value, set_min_numeric_value, clear_min_numeric_value),
(MaxNumericValue, max_numeric_value, set_max_numeric_value, clear_max_numeric_value),
(NumericValueStep, numeric_value_step, set_numeric_value_step, clear_numeric_value_step),
(NumericValueJump, numeric_value_jump, set_numeric_value_jump, clear_numeric_value_jump),
(FontSize, font_size, set_font_size, clear_font_size),
(FontWeight, font_weight, set_font_weight, clear_font_weight)
}
usize_property_methods! {
(RowCount, row_count, set_row_count, clear_row_count),
(ColumnCount, column_count, set_column_count, clear_column_count),
(RowIndex, row_index, set_row_index, clear_row_index),
(ColumnIndex, column_index, set_column_index, clear_column_index),
(RowSpan, row_span, set_row_span, clear_row_span),
(ColumnSpan, column_span, set_column_span, clear_column_span),
(Level, level, set_level, clear_level),
(SizeOfSet, size_of_set, set_size_of_set, clear_size_of_set),
(PositionInSet, position_in_set, set_position_in_set, clear_position_in_set)
}
color_property_methods! {
(ColorValue, color_value, set_color_value, clear_color_value),
(BackgroundColor, background_color, set_background_color, clear_background_color),
(ForegroundColor, foreground_color, set_foreground_color, clear_foreground_color)
}
text_decoration_property_methods! {
(Overline, overline, set_overline, clear_overline),
(Strikethrough, strikethrough, set_strikethrough, clear_strikethrough),
(Underline, underline, set_underline, clear_underline)
}
length_slice_property_methods! {
(CharacterLengths, character_lengths, set_character_lengths, clear_character_lengths),
(WordLengths, word_lengths, set_word_lengths, clear_word_lengths)
}
coord_slice_property_methods! {
(CharacterPositions, character_positions, set_character_positions, clear_character_positions),
(CharacterWidths, character_widths, set_character_widths, clear_character_widths)
}
bool_property_methods! {
(Expanded, is_expanded, set_expanded, clear_expanded),
(Selected, is_selected, set_selected, clear_selected)
}
unique_enum_property_methods! {
(Invalid, invalid, set_invalid, clear_invalid),
(Toggled, toggled, set_toggled, clear_toggled),
(Live, live, set_live, clear_live),
(TextDirection, text_direction, set_text_direction, clear_text_direction),
(Orientation, orientation, set_orientation, clear_orientation),
(SortDirection, sort_direction, set_sort_direction, clear_sort_direction),
(AriaCurrent, aria_current, set_aria_current, clear_aria_current),
(AutoComplete, auto_complete, set_auto_complete, clear_auto_complete),
(HasPopup, has_popup, set_has_popup, clear_has_popup),
(ListStyle, list_style, set_list_style, clear_list_style),
(TextAlign, text_align, set_text_align, clear_text_align),
(VerticalOffset, vertical_offset, set_vertical_offset, clear_vertical_offset)
}
property_methods! {
(Transform, transform, get_affine_property, Option<&Affine>, set_transform, set_affine_property, impl Into<Box<Affine>>, clear_transform),
(Bounds, bounds, get_rect_property, Option<Rect>, set_bounds, set_rect_property, Rect, clear_bounds),
(TextSelection, text_selection, get_text_selection_property, Option<&TextSelection>, set_text_selection, set_text_selection_property, impl Into<Box<TextSelection>>, clear_text_selection)
}
impl FrozenNode {
option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection,] }
}
impl Node {
option_properties_debug_method! { debug_option_properties, [transform, bounds, text_selection,] }
}
vec_property_methods! {
(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)
}
impl fmt::Debug for FrozenNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut fmt = f.debug_struct("FrozenNode");
fmt.field("role", &self.role());
let supported_actions = action_mask_to_action_vec(self.actions);
if !supported_actions.is_empty() {
fmt.field("actions", &supported_actions);
}
self.debug_flag_properties(&mut fmt);
self.debug_node_id_vec_properties(&mut fmt);
self.debug_node_id_properties(&mut fmt);
self.debug_string_properties(&mut fmt);
self.debug_f64_properties(&mut fmt);
self.debug_usize_properties(&mut fmt);
self.debug_color_properties(&mut fmt);
self.debug_text_decoration_properties(&mut fmt);
self.debug_length_slice_properties(&mut fmt);
self.debug_coord_slice_properties(&mut fmt);
self.debug_bool_properties(&mut fmt);
self.debug_unique_enum_properties(&mut fmt);
self.debug_option_properties(&mut fmt);
let custom_actions = self.custom_actions();
if !custom_actions.is_empty() {
fmt.field("custom_actions", &custom_actions);
}
fmt.finish()
}
}
impl fmt::Debug for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut fmt = f.debug_struct("Node");
fmt.field("role", &self.role());
let supported_actions = action_mask_to_action_vec(self.actions);
if !supported_actions.is_empty() {
fmt.field("actions", &supported_actions);
}
self.debug_flag_properties(&mut fmt);
self.debug_node_id_vec_properties(&mut fmt);
self.debug_node_id_properties(&mut fmt);
self.debug_string_properties(&mut fmt);
self.debug_f64_properties(&mut fmt);
self.debug_usize_properties(&mut fmt);
self.debug_color_properties(&mut fmt);
self.debug_text_decoration_properties(&mut fmt);
self.debug_length_slice_properties(&mut fmt);
self.debug_coord_slice_properties(&mut fmt);
self.debug_bool_properties(&mut fmt);
self.debug_unique_enum_properties(&mut fmt);
self.debug_option_properties(&mut fmt);
let custom_actions = self.custom_actions();
if !custom_actions.is_empty() {
fmt.field("custom_actions", &custom_actions);
}
fmt.finish()
}
}
#[cfg(feature = "serde")]
macro_rules! serialize_property {
($self:ident, $map:ident, $index:ident, $id:ident, { $($variant:ident),+ }) => {
match &$self.values[$index as usize] {
PropertyValue::None => (),
$(PropertyValue::$variant(value) => {
$map.serialize_entry(&$id, &value)?;
})*
}
}
}
#[cfg(feature = "serde")]
macro_rules! deserialize_property {
($props:ident, $map:ident, $key:ident, { $($type:ident { $($id:ident),+ }),+ }) => {
match $key {
$($(PropertyId::$id => {
let value = $map.next_value()?;
$props.set(PropertyId::$id, PropertyValue::$type(value));
})*)*
PropertyId::Unset => {
let _ = $map.next_value::<IgnoredAny>()?;
}
}
}
}
#[cfg(feature = "serde")]
impl Serialize for Properties {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut len = 0;
for value in &*self.values {
if !matches!(*value, PropertyValue::None) {
len += 1;
}
}
let mut map = serializer.serialize_map(Some(len))?;
for (id, index) in self.indices.0.iter().copied().enumerate() {
if index == PropertyId::Unset as u8 {
continue;
}
let id = PropertyId::n(id as _).unwrap();
serialize_property!(self, map, index, id, {
NodeIdVec,
NodeId,
String,
F64,
Usize,
Color,
TextDecoration,
LengthSlice,
CoordSlice,
Bool,
Invalid,
Toggled,
Live,
TextDirection,
Orientation,
SortDirection,
AriaCurrent,
AutoComplete,
HasPopup,
ListStyle,
TextAlign,
VerticalOffset,
Affine,
Rect,
TextSelection,
CustomActionVec
});
}
map.end()
}
}
#[cfg(feature = "serde")]
struct PropertiesVisitor;
#[cfg(feature = "serde")]
impl<'de> Visitor<'de> for PropertiesVisitor {
type Value = Properties;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("property map")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'de>,
{
let mut props = Properties::default();
while let Some(id) = map.next_key()? {
deserialize_property!(props, map, id, {
NodeIdVec {
Children,
Controls,
Details,
DescribedBy,
FlowTo,
LabelledBy,
Owns,
RadioGroup
},
NodeId {
ActiveDescendant,
ErrorMessage,
InPageLinkTarget,
MemberOf,
NextOnLine,
PreviousOnLine,
PopupFor
},
String {
Label,
Description,
Value,
AccessKey,
AuthorId,
ClassName,
FontFamily,
HtmlTag,
InnerHtml,
KeyboardShortcut,
Language,
Placeholder,
RoleDescription,
StateDescription,
Tooltip,
Url,
RowIndexText,
ColumnIndexText
},
F64 {
ScrollX,
ScrollXMin,
ScrollXMax,
ScrollY,
ScrollYMin,
ScrollYMax,
NumericValue,
MinNumericValue,
MaxNumericValue,
NumericValueStep,
NumericValueJump,
FontSize,
FontWeight
},
Usize {
RowCount,
ColumnCount,
RowIndex,
ColumnIndex,
RowSpan,
ColumnSpan,
Level,
SizeOfSet,
PositionInSet
},
Color {
ColorValue,
BackgroundColor,
ForegroundColor
},
TextDecoration {
Overline,
Strikethrough,
Underline
},
LengthSlice {
CharacterLengths,
WordLengths
},
CoordSlice {
CharacterPositions,
CharacterWidths
},
Bool {
Expanded,
Selected
},
Invalid { Invalid },
Toggled { Toggled },
Live { Live },
TextDirection { TextDirection },
Orientation { Orientation },
SortDirection { SortDirection },
AriaCurrent { AriaCurrent },
AutoComplete { AutoComplete },
HasPopup { HasPopup },
ListStyle { ListStyle },
TextAlign { TextAlign },
VerticalOffset { VerticalOffset },
Affine { Transform },
Rect { Bounds },
TextSelection { TextSelection },
CustomActionVec { CustomActions }
});
}
Ok(props)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Properties {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(PropertiesVisitor)
}
}
#[cfg(feature = "schemars")]
macro_rules! add_schema_property {
($gen:ident, $properties:ident, $enum_value:expr, $type:ty) => {{
let name = format!("{:?}", $enum_value);
let name = name[..1].to_ascii_lowercase() + &name[1..];
let subschema = $gen.subschema_for::<$type>();
$properties.insert(name, subschema);
}};
}
#[cfg(feature = "schemars")]
macro_rules! add_properties_to_schema {
($gen:ident, $properties:ident, { $($type:ty { $($id:ident),+ }),+ }) => {
$($(add_schema_property!($gen, $properties, PropertyId::$id, $type);)*)*
}
}
#[cfg(feature = "schemars")]
impl JsonSchema for Properties {
#[inline]
fn schema_name() -> String {
"Properties".into()
}
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
let mut properties = SchemaMap::<String, Schema>::new();
add_properties_to_schema!(gen, properties, {
Vec<NodeId> {
Children,
Controls,
Details,
DescribedBy,
FlowTo,
LabelledBy,
Owns,
RadioGroup
},
NodeId {
ActiveDescendant,
ErrorMessage,
InPageLinkTarget,
MemberOf,
NextOnLine,
PreviousOnLine,
PopupFor
},
Box<str> {
Label,
Description,
Value,
AccessKey,
AuthorId,
ClassName,
FontFamily,
HtmlTag,
InnerHtml,
KeyboardShortcut,
Language,
Placeholder,
RoleDescription,
StateDescription,
Tooltip,
Url,
RowIndexText,
ColumnIndexText
},
f64 {
ScrollX,
ScrollXMin,
ScrollXMax,
ScrollY,
ScrollYMin,
ScrollYMax,
NumericValue,
MinNumericValue,
MaxNumericValue,
NumericValueStep,
NumericValueJump,
FontSize,
FontWeight
},
usize {
RowCount,
ColumnCount,
RowIndex,
ColumnIndex,
RowSpan,
ColumnSpan,
Level,
SizeOfSet,
PositionInSet
},
u32 {
ColorValue,
BackgroundColor,
ForegroundColor
},
TextDecoration {
Overline,
Strikethrough,
Underline
},
Box<[u8]> {
CharacterLengths,
WordLengths
},
Box<[f32]> {
CharacterPositions,
CharacterWidths
},
bool {
Expanded,
Selected
},
Invalid { Invalid },
Toggled { Toggled },
Live { Live },
TextDirection { TextDirection },
Orientation { Orientation },
SortDirection { SortDirection },
AriaCurrent { AriaCurrent },
AutoComplete { AutoComplete },
HasPopup { HasPopup },
ListStyle { ListStyle },
TextAlign { TextAlign },
VerticalOffset { VerticalOffset },
Affine { Transform },
Rect { Bounds },
TextSelection { TextSelection },
Vec<CustomAction> { CustomActions }
});
SchemaObject {
instance_type: Some(InstanceType::Object.into()),
object: Some(
ObjectValidation {
properties,
..Default::default()
}
.into(),
),
..Default::default()
}
.into()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Tree {
pub root: NodeId,
pub app_name: Option<String>,
pub toolkit_name: Option<String>,
pub toolkit_version: Option<String>,
}
impl Tree {
#[inline]
pub fn new(root: NodeId) -> Tree {
Tree {
root,
app_name: None,
toolkit_name: None,
toolkit_version: None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct TreeUpdate {
pub nodes: Vec<(NodeId, Node)>,
pub tree: Option<Tree>,
pub focus: NodeId,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[repr(C)]
pub enum ActionData {
CustomAction(i32),
Value(Box<str>),
NumericValue(f64),
ScrollTargetRect(Rect),
ScrollToPoint(Point),
SetScrollOffset(Point),
SetTextSelection(TextSelection),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct ActionRequest {
pub action: Action,
pub target: NodeId,
pub data: Option<ActionData>,
}
pub trait ActivationHandler {
fn request_initial_tree(&mut self) -> Option<TreeUpdate>;
}
pub trait ActionHandler {
fn do_action(&mut self, request: ActionRequest);
}
pub trait DeactivationHandler {
fn deactivate_accessibility(&mut self);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn action_n() {
assert_eq!(Action::n(0), Some(Action::Click));
assert_eq!(Action::n(1), Some(Action::Focus));
assert_eq!(Action::n(2), Some(Action::Blur));
assert_eq!(Action::n(3), Some(Action::Collapse));
assert_eq!(Action::n(4), Some(Action::Expand));
assert_eq!(Action::n(5), Some(Action::CustomAction));
assert_eq!(Action::n(6), Some(Action::Decrement));
assert_eq!(Action::n(7), Some(Action::Increment));
assert_eq!(Action::n(8), Some(Action::HideTooltip));
assert_eq!(Action::n(9), Some(Action::ShowTooltip));
assert_eq!(Action::n(10), Some(Action::ReplaceSelectedText));
assert_eq!(Action::n(11), Some(Action::ScrollBackward));
assert_eq!(Action::n(12), Some(Action::ScrollDown));
assert_eq!(Action::n(13), Some(Action::ScrollForward));
assert_eq!(Action::n(14), Some(Action::ScrollLeft));
assert_eq!(Action::n(15), Some(Action::ScrollRight));
assert_eq!(Action::n(16), Some(Action::ScrollUp));
assert_eq!(Action::n(17), Some(Action::ScrollIntoView));
assert_eq!(Action::n(18), Some(Action::ScrollToPoint));
assert_eq!(Action::n(19), Some(Action::SetScrollOffset));
assert_eq!(Action::n(20), Some(Action::SetTextSelection));
assert_eq!(
Action::n(21),
Some(Action::SetSequentialFocusNavigationStartingPoint)
);
assert_eq!(Action::n(22), Some(Action::SetValue));
assert_eq!(Action::n(23), Some(Action::ShowContextMenu));
assert_eq!(Action::n(24), None);
}
#[test]
fn test_action_mask_to_action_vec() {
assert_eq!(
Vec::<Action>::new(),
action_mask_to_action_vec(Node::new(Role::Unknown).actions)
);
let mut node = Node::new(Role::Unknown);
node.add_action(Action::Click);
assert_eq!(
&[Action::Click],
action_mask_to_action_vec(node.actions).as_slice()
);
let mut node = Node::new(Role::Unknown);
node.add_action(Action::ShowContextMenu);
assert_eq!(
&[Action::ShowContextMenu],
action_mask_to_action_vec(node.actions).as_slice()
);
let mut node = Node::new(Role::Unknown);
node.add_action(Action::Click);
node.add_action(Action::ShowContextMenu);
assert_eq!(
&[Action::Click, Action::ShowContextMenu],
action_mask_to_action_vec(node.actions).as_slice()
);
let mut node = Node::new(Role::Unknown);
node.add_action(Action::Focus);
node.add_action(Action::Blur);
node.add_action(Action::Collapse);
assert_eq!(
&[Action::Focus, Action::Blur, Action::Collapse],
action_mask_to_action_vec(node.actions).as_slice()
);
}
}