1 | // Copyright 2024 The AccessKit Authors. All rights reserved. |
2 | // Licensed under the Apache License, Version 2.0 (found in |
3 | // the LICENSE-APACHE file) or the MIT license (found in |
4 | // the LICENSE-MIT file), at your option. |
5 | |
6 | //! API that corresponds more closely to the libatspi client API, |
7 | //! intended to be used by bindings to languages with less rich |
8 | //! type systems. |
9 | |
10 | use crate::{ |
11 | Adapter, Event as EventEnum, NodeIdOrRoot, ObjectEvent, PlatformNode, PlatformRoot, Property, |
12 | WindowEvent, |
13 | }; |
14 | |
15 | pub use crate::{CoordType, Error, Layer, Rect, Result, Role, StateSet}; |
16 | |
17 | #[derive (Clone, Hash, PartialEq)] |
18 | pub enum Accessible { |
19 | Node(PlatformNode), |
20 | Root(PlatformRoot), |
21 | } |
22 | |
23 | impl Accessible { |
24 | pub fn role(&self) -> Result<Role> { |
25 | match self { |
26 | Self::Node(node) => node.role(), |
27 | Self::Root(_) => Ok(Role::Application), |
28 | } |
29 | } |
30 | |
31 | pub fn localized_role_name(&self) -> Result<String> { |
32 | match self { |
33 | Self::Node(node) => node.localized_role_name(), |
34 | Self::Root(_) => Ok("" .into()), |
35 | } |
36 | } |
37 | |
38 | pub fn name(&self) -> Result<String> { |
39 | match self { |
40 | Self::Node(node) => node.name(), |
41 | Self::Root(root) => root.name(), |
42 | } |
43 | } |
44 | |
45 | pub fn description(&self) -> Result<String> { |
46 | match self { |
47 | Self::Node(node) => node.description(), |
48 | Self::Root(_) => Ok("" .into()), |
49 | } |
50 | } |
51 | |
52 | pub fn state(&self) -> StateSet { |
53 | match self { |
54 | Self::Node(node) => node.state(), |
55 | Self::Root(_) => StateSet::empty(), |
56 | } |
57 | } |
58 | |
59 | pub fn parent(&self) -> Result<Option<Self>> { |
60 | match self { |
61 | Self::Node(node) => match node.parent()? { |
62 | NodeIdOrRoot::Node(id) => Ok(Some(Self::Node(node.relative(id)))), |
63 | NodeIdOrRoot::Root => node.root().map(|root| Some(Self::Root(root))), |
64 | }, |
65 | Self::Root(_) => Ok(None), |
66 | } |
67 | } |
68 | |
69 | pub fn index_in_parent(&self) -> Result<i32> { |
70 | match self { |
71 | Self::Node(node) => node.index_in_parent(), |
72 | Self::Root(_) => Ok(-1), |
73 | } |
74 | } |
75 | |
76 | pub fn child_count(&self) -> Result<i32> { |
77 | match self { |
78 | Self::Node(node) => node.child_count(), |
79 | Self::Root(root) => root.child_count(), |
80 | } |
81 | } |
82 | |
83 | pub fn child_at_index(&self, index: usize) -> Result<Option<Self>> { |
84 | match self { |
85 | Self::Node(node) => node |
86 | .child_at_index(index) |
87 | .map(|id| id.map(|id| Self::Node(node.relative(id)))), |
88 | Self::Root(root) => root |
89 | .child_at_index(index) |
90 | .map(|child| child.map(Self::Node)), |
91 | } |
92 | } |
93 | |
94 | pub fn map_children<T, I>(&self, f: impl Fn(Self) -> I) -> Result<T> |
95 | where |
96 | T: FromIterator<I>, |
97 | { |
98 | match self { |
99 | Self::Node(node) => node.map_children(|id| f(Self::Node(node.relative(id)))), |
100 | Self::Root(root) => root.map_children(|node| f(Self::Node(node))), |
101 | } |
102 | } |
103 | |
104 | pub fn application(&self) -> Result<Self> { |
105 | match self { |
106 | Self::Node(node) => node.root().map(Self::Root), |
107 | Self::Root(root) => Ok(Self::Root(root.clone())), |
108 | } |
109 | } |
110 | |
111 | pub fn toolkit_name(&self) -> Result<String> { |
112 | match self { |
113 | Self::Node(node) => node.toolkit_name(), |
114 | Self::Root(root) => root.toolkit_name(), |
115 | } |
116 | } |
117 | |
118 | pub fn toolkit_version(&self) -> Result<String> { |
119 | match self { |
120 | Self::Node(node) => node.toolkit_version(), |
121 | Self::Root(root) => root.toolkit_version(), |
122 | } |
123 | } |
124 | |
125 | pub fn supports_action(&self) -> Result<bool> { |
126 | match self { |
127 | Self::Node(node) => node.supports_action(), |
128 | Self::Root(_) => Ok(false), |
129 | } |
130 | } |
131 | |
132 | pub fn n_actions(&self) -> Result<i32> { |
133 | match self { |
134 | Self::Node(node) => node.n_actions(), |
135 | Self::Root(_) => Err(Error::UnsupportedInterface), |
136 | } |
137 | } |
138 | |
139 | pub fn action_name(&self, index: i32) -> Result<String> { |
140 | match self { |
141 | Self::Node(node) => node.action_name(index), |
142 | Self::Root(_) => Err(Error::UnsupportedInterface), |
143 | } |
144 | } |
145 | |
146 | pub fn do_action(&self, index: i32) -> Result<bool> { |
147 | match self { |
148 | Self::Node(node) => node.do_action(index), |
149 | Self::Root(_) => Err(Error::UnsupportedInterface), |
150 | } |
151 | } |
152 | |
153 | pub fn supports_component(&self) -> Result<bool> { |
154 | match self { |
155 | Self::Node(node) => node.supports_component(), |
156 | Self::Root(_) => Ok(false), |
157 | } |
158 | } |
159 | |
160 | pub fn contains(&self, x: i32, y: i32, coord_type: CoordType) -> Result<bool> { |
161 | match self { |
162 | Self::Node(node) => node.contains(x, y, coord_type), |
163 | Self::Root(_) => Err(Error::UnsupportedInterface), |
164 | } |
165 | } |
166 | |
167 | pub fn accessible_at_point( |
168 | &self, |
169 | x: i32, |
170 | y: i32, |
171 | coord_type: CoordType, |
172 | ) -> Result<Option<Self>> { |
173 | match self { |
174 | Self::Node(node) => node |
175 | .accessible_at_point(x, y, coord_type) |
176 | .map(|id| id.map(|id| Self::Node(node.relative(id)))), |
177 | Self::Root(_) => Err(Error::UnsupportedInterface), |
178 | } |
179 | } |
180 | |
181 | pub fn extents(&self, coord_type: CoordType) -> Result<Rect> { |
182 | match self { |
183 | Self::Node(node) => node.extents(coord_type), |
184 | Self::Root(_) => Err(Error::UnsupportedInterface), |
185 | } |
186 | } |
187 | |
188 | pub fn layer(&self) -> Result<Layer> { |
189 | match self { |
190 | Self::Node(node) => node.layer(), |
191 | Self::Root(_) => Err(Error::UnsupportedInterface), |
192 | } |
193 | } |
194 | |
195 | pub fn grab_focus(&self) -> Result<bool> { |
196 | match self { |
197 | Self::Node(node) => node.grab_focus(), |
198 | Self::Root(_) => Err(Error::UnsupportedInterface), |
199 | } |
200 | } |
201 | |
202 | pub fn scroll_to_point(&self, coord_type: CoordType, x: i32, y: i32) -> Result<bool> { |
203 | match self { |
204 | Self::Node(node) => node.scroll_to_point(coord_type, x, y), |
205 | Self::Root(_) => Err(Error::UnsupportedInterface), |
206 | } |
207 | } |
208 | |
209 | pub fn supports_value(&self) -> Result<bool> { |
210 | match self { |
211 | Self::Node(node) => node.supports_value(), |
212 | Self::Root(_) => Ok(false), |
213 | } |
214 | } |
215 | |
216 | pub fn minimum_value(&self) -> Result<f64> { |
217 | match self { |
218 | Self::Node(node) => node.minimum_value(), |
219 | Self::Root(_) => Err(Error::UnsupportedInterface), |
220 | } |
221 | } |
222 | |
223 | pub fn maximum_value(&self) -> Result<f64> { |
224 | match self { |
225 | Self::Node(node) => node.maximum_value(), |
226 | Self::Root(_) => Err(Error::UnsupportedInterface), |
227 | } |
228 | } |
229 | |
230 | pub fn minimum_increment(&self) -> Result<f64> { |
231 | match self { |
232 | Self::Node(node) => node.minimum_increment(), |
233 | Self::Root(_) => Err(Error::UnsupportedInterface), |
234 | } |
235 | } |
236 | |
237 | pub fn current_value(&self) -> Result<f64> { |
238 | match self { |
239 | Self::Node(node) => node.current_value(), |
240 | Self::Root(_) => Err(Error::UnsupportedInterface), |
241 | } |
242 | } |
243 | |
244 | pub fn set_current_value(&self, value: f64) -> Result<()> { |
245 | match self { |
246 | Self::Node(node) => node.set_current_value(value), |
247 | Self::Root(_) => Err(Error::UnsupportedInterface), |
248 | } |
249 | } |
250 | } |
251 | |
252 | #[derive (PartialEq)] |
253 | pub enum EventData { |
254 | U32(u32), |
255 | F64(f64), |
256 | String(String), |
257 | Rect(Rect), |
258 | Accessible(Accessible), |
259 | } |
260 | |
261 | #[derive (PartialEq)] |
262 | pub struct Event { |
263 | pub kind: String, |
264 | pub source: Accessible, |
265 | pub detail1: i32, |
266 | pub detail2: i32, |
267 | pub data: Option<EventData>, |
268 | } |
269 | |
270 | impl Event { |
271 | pub fn new(adapter: &Adapter, event: EventEnum) -> Self { |
272 | match event { |
273 | EventEnum::Object { target, event } => { |
274 | let source = match target { |
275 | NodeIdOrRoot::Node(target) => Accessible::Node(adapter.platform_node(target)), |
276 | NodeIdOrRoot::Root => Accessible::Root(adapter.platform_root()), |
277 | }; |
278 | match event { |
279 | ObjectEvent::ActiveDescendantChanged(child) => { |
280 | let child = Accessible::Node(adapter.platform_node(child)); |
281 | Self { |
282 | kind: "object:active-descendant-changed" .into(), |
283 | source, |
284 | detail1: 0, |
285 | detail2: 0, |
286 | data: Some(EventData::Accessible(child)), |
287 | } |
288 | } |
289 | ObjectEvent::Announcement(message, politeness) => Self { |
290 | kind: "object:announcement" .into(), |
291 | source, |
292 | detail1: politeness as i32, |
293 | detail2: 0, |
294 | data: Some(EventData::String(message)), |
295 | }, |
296 | ObjectEvent::BoundsChanged(bounds) => Self { |
297 | kind: "object:bounds-changed" .into(), |
298 | source, |
299 | detail1: 0, |
300 | detail2: 0, |
301 | data: Some(EventData::Rect(bounds)), |
302 | }, |
303 | ObjectEvent::ChildAdded(index, child) => { |
304 | let child = Accessible::Node(adapter.platform_node(child)); |
305 | Self { |
306 | kind: "object:children-changed:add" .into(), |
307 | source, |
308 | detail1: index as i32, |
309 | detail2: 0, |
310 | data: Some(EventData::Accessible(child)), |
311 | } |
312 | } |
313 | ObjectEvent::ChildRemoved(child) => { |
314 | let child = Accessible::Node(adapter.platform_node(child)); |
315 | Self { |
316 | kind: "object:children-changed:remove" .into(), |
317 | source, |
318 | detail1: -1, |
319 | detail2: 0, |
320 | data: Some(EventData::Accessible(child)), |
321 | } |
322 | } |
323 | ObjectEvent::PropertyChanged(property) => Self { |
324 | kind: match property { |
325 | Property::Name(_) => "object:property-change:accessible-name" , |
326 | Property::Description(_) => { |
327 | "object:property-change:accessible-description" |
328 | } |
329 | Property::Parent(_) => "object:property-change:accessible-parent" , |
330 | Property::Role(_) => "object:property-change:accessible-role" , |
331 | Property::Value(_) => "object:property-change:accessible-value" , |
332 | } |
333 | .into(), |
334 | source, |
335 | detail1: 0, |
336 | detail2: 0, |
337 | data: Some(match property { |
338 | Property::Name(value) => EventData::String(value), |
339 | Property::Description(value) => EventData::String(value), |
340 | Property::Parent(parent) => { |
341 | let parent = match parent { |
342 | NodeIdOrRoot::Node(parent) => { |
343 | Accessible::Node(adapter.platform_node(parent)) |
344 | } |
345 | NodeIdOrRoot::Root => Accessible::Root(adapter.platform_root()), |
346 | }; |
347 | EventData::Accessible(parent) |
348 | } |
349 | Property::Role(value) => EventData::U32(value as u32), |
350 | Property::Value(value) => EventData::F64(value), |
351 | }), |
352 | }, |
353 | ObjectEvent::StateChanged(state, value) => Self { |
354 | kind: format!("object:state-changed: {}" , String::from(state)), |
355 | source, |
356 | detail1: value as i32, |
357 | detail2: 0, |
358 | data: None, |
359 | }, |
360 | } |
361 | } |
362 | EventEnum::Window { |
363 | target, |
364 | name, |
365 | event, |
366 | } => { |
367 | let kind = match event { |
368 | WindowEvent::Activated => "window:activate" , |
369 | WindowEvent::Deactivated => "window:deactivate" , |
370 | }; |
371 | let source = Accessible::Node(adapter.platform_node(target)); |
372 | Self { |
373 | kind: kind.into(), |
374 | source, |
375 | detail1: 0, |
376 | detail2: 0, |
377 | data: Some(EventData::String(name)), |
378 | } |
379 | } |
380 | } |
381 | } |
382 | } |
383 | |