i_slint_core/
accessibility.rs1use crate::{
7 SharedString,
8 item_tree::ItemTreeVTable,
9 items::{ItemRc, TextInput},
10};
11use alloc::{vec, vec::Vec};
12use bitflags::bitflags;
13use vtable::VRcMapped;
14
15#[repr(u32)]
17#[derive(PartialEq, Eq, Copy, Clone, strum::Display)]
18#[strum(serialize_all = "kebab-case")]
19pub enum AccessibleStringProperty {
20 Checkable,
21 Checked,
22 DelegateFocus,
23 Description,
24 Enabled,
25 Expandable,
26 Expanded,
27 Id,
28 ItemCount,
29 ItemIndex,
30 ItemSelectable,
31 ItemSelected,
32 Label,
33 LiveRegion,
34 Orientation,
35 PlaceholderText,
36 ReadOnly,
37 Value,
38 ValueMaximum,
39 ValueMinimum,
40 ValueStep,
41}
42
43#[repr(u32)]
45#[derive(PartialEq, Clone)]
46pub enum AccessibilityAction {
47 Default,
48 Decrement,
49 Increment,
50 Expand,
51 ReplaceSelectedText(SharedString),
53 SetValue(SharedString),
54}
55
56bitflags! {
57 #[repr(transparent)]
59 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
60 pub struct SupportedAccessibilityAction: u32 {
61 const Default = 1;
62 const Decrement = 1 << 1;
63 const Increment = 1 << 2;
64 const Expand = 1 << 3;
65 const ReplaceSelectedText = 1 << 4;
66 const SetValue = 1 << 5;
67 }
68}
69
70pub fn accessible_descendents(root_item: &ItemRc) -> impl Iterator<Item = ItemRc> {
75 fn try_candidate_or_find_next_accessible_descendent(
76 candidate: ItemRc,
77 descendent_candidates: &mut Vec<ItemRc>,
78 ) -> Option<ItemRc> {
79 if candidate.is_accessible() {
80 return Some(candidate);
81 }
82
83 candidate.first_child().and_then(|child| {
84 if let Some(next) = child.next_sibling() {
85 descendent_candidates.push(next);
86 }
87 try_candidate_or_find_next_accessible_descendent(child, descendent_candidates)
88 })
89 }
90
91 let mut descendent_candidates = Vec::new();
94 if let Some(child) = root_item.first_child() {
95 descendent_candidates.push(child);
96 }
97
98 core::iter::from_fn(move || {
99 loop {
100 let candidate = descendent_candidates.pop()?;
101
102 if let Some(next_candidate) = candidate.next_sibling() {
103 descendent_candidates.push(next_candidate);
104 }
105
106 if let Some(descendent) = try_candidate_or_find_next_accessible_descendent(
107 candidate,
108 &mut descendent_candidates,
109 ) {
110 return Some(descendent);
111 }
112 }
113 })
114}
115
116pub fn find_text_input(item: &ItemRc) -> Option<VRcMapped<ItemTreeVTable, TextInput>> {
118 fn try_candidate_or_find_next_descendent(
119 candidate: ItemRc,
120 descendent_candidates: &mut Vec<ItemRc>,
121 ) -> Option<VRcMapped<ItemTreeVTable, TextInput>> {
122 if let Some(input) = candidate.downcast::<TextInput>() {
123 return Some(input);
124 }
125
126 candidate.first_child().and_then(|child| {
127 if let Some(next) = child.next_sibling() {
128 descendent_candidates.push(next);
129 }
130 try_candidate_or_find_next_descendent(child, descendent_candidates)
131 })
132 }
133
134 let mut descendent_candidates = vec![item.clone()];
135
136 loop {
137 let candidate = descendent_candidates.pop()?;
138
139 if let Some(next_candidate) = candidate.next_sibling() {
140 descendent_candidates.push(next_candidate);
141 }
142
143 if let Some(input) =
144 try_candidate_or_find_next_descendent(candidate, &mut descendent_candidates)
145 {
146 return Some(input);
147 }
148 }
149}