1use accesskit::{
12 Action, ActionData, ActionRequest, Affine, DefaultActionVerb, Live, NodeId, Orientation, Point,
13 Rect, Role, Toggled,
14};
15use accesskit_consumer::{FilterResult, Node, TreeState};
16use atspi_common::{
17 CoordType, Granularity, Interface, InterfaceSet, Layer, Live as AtspiLive, Role as AtspiRole,
18 ScrollType, State, StateSet,
19};
20use std::{
21 collections::HashMap,
22 hash::{Hash, Hasher},
23 iter::FusedIterator,
24 sync::{Arc, RwLock, RwLockReadGuard, Weak},
25};
26
27use crate::{
28 adapter::Adapter,
29 context::{AppContext, Context},
30 filters::filter,
31 util::*,
32 Action as AtspiAction, Error, ObjectEvent, Property, Rect as AtspiRect, Result,
33};
34
35pub(crate) struct NodeWrapper<'a>(pub(crate) &'a Node<'a>);
36
37impl<'a> NodeWrapper<'a> {
38 pub(crate) fn name(&self) -> Option<String> {
39 self.0.name()
40 }
41
42 pub(crate) fn description(&self) -> Option<String> {
43 self.0.description()
44 }
45
46 pub(crate) fn parent_id(&self) -> Option<NodeId> {
47 self.0.parent_id()
48 }
49
50 pub(crate) fn id(&self) -> NodeId {
51 self.0.id()
52 }
53
54 fn filtered_child_ids(
55 &self,
56 ) -> impl DoubleEndedIterator<Item = NodeId> + FusedIterator<Item = NodeId> + '_ {
57 self.0.filtered_children(&filter).map(|child| child.id())
58 }
59
60 pub(crate) fn role(&self) -> AtspiRole {
61 if self.0.has_role_description() {
62 return AtspiRole::Extended;
63 }
64
65 match self.0.role() {
66 Role::Alert => AtspiRole::Notification,
67 Role::AlertDialog => AtspiRole::Alert,
68 Role::Comment | Role::Suggestion => AtspiRole::Section,
69 Role::Application => AtspiRole::Embedded,
71 Role::Article => AtspiRole::Article,
72 Role::Audio => AtspiRole::Audio,
73 Role::Banner | Role::Header => AtspiRole::Landmark,
74 Role::Blockquote => AtspiRole::BlockQuote,
75 Role::Caret => AtspiRole::Unknown,
76 Role::Button => {
77 if self.0.toggled().is_some() {
78 AtspiRole::ToggleButton
79 } else {
80 AtspiRole::PushButton
81 }
82 }
83 Role::DefaultButton => AtspiRole::PushButton,
84 Role::Canvas => AtspiRole::Canvas,
85 Role::Caption => AtspiRole::Caption,
86 Role::Cell => AtspiRole::TableCell,
87 Role::CheckBox => AtspiRole::CheckBox,
88 Role::Switch => AtspiRole::ToggleButton,
89 Role::ColorWell => AtspiRole::PushButton,
90 Role::ColumnHeader => AtspiRole::ColumnHeader,
91 Role::ComboBox | Role::EditableComboBox => AtspiRole::ComboBox,
92 Role::Complementary => AtspiRole::Landmark,
93 Role::ContentDeletion => AtspiRole::ContentDeletion,
94 Role::ContentInsertion => AtspiRole::ContentInsertion,
95 Role::ContentInfo | Role::Footer => AtspiRole::Landmark,
96 Role::Definition | Role::DescriptionListDetail => AtspiRole::DescriptionValue,
97 Role::DescriptionList => AtspiRole::DescriptionList,
98 Role::DescriptionListTerm => AtspiRole::DescriptionTerm,
99 Role::Details => AtspiRole::Panel,
100 Role::Dialog => AtspiRole::Dialog,
101 Role::Directory => AtspiRole::List,
102 Role::DisclosureTriangle => AtspiRole::ToggleButton,
103 Role::DocCover => AtspiRole::Image,
104 Role::DocBackLink | Role::DocBiblioRef | Role::DocGlossRef | Role::DocNoteRef => {
105 AtspiRole::Link
106 }
107 Role::DocBiblioEntry | Role::DocEndnote => AtspiRole::ListItem,
108 Role::DocNotice | Role::DocTip => AtspiRole::Comment,
109 Role::DocFootnote => AtspiRole::Footnote,
110 Role::DocPageBreak => AtspiRole::Separator,
111 Role::DocPageFooter => AtspiRole::Footer,
112 Role::DocPageHeader => AtspiRole::Header,
113 Role::DocAcknowledgements
114 | Role::DocAfterword
115 | Role::DocAppendix
116 | Role::DocBibliography
117 | Role::DocChapter
118 | Role::DocConclusion
119 | Role::DocCredits
120 | Role::DocEndnotes
121 | Role::DocEpilogue
122 | Role::DocErrata
123 | Role::DocForeword
124 | Role::DocGlossary
125 | Role::DocIndex
126 | Role::DocIntroduction
127 | Role::DocPageList
128 | Role::DocPart
129 | Role::DocPreface
130 | Role::DocPrologue
131 | Role::DocToc => AtspiRole::Landmark,
132 Role::DocAbstract
133 | Role::DocColophon
134 | Role::DocCredit
135 | Role::DocDedication
136 | Role::DocEpigraph
137 | Role::DocExample
138 | Role::DocPullquote
139 | Role::DocQna => AtspiRole::Section,
140 Role::DocSubtitle => AtspiRole::Heading,
141 Role::Document => AtspiRole::DocumentFrame,
142 Role::EmbeddedObject => AtspiRole::Embedded,
143 Role::Form => AtspiRole::Form,
147 Role::Figure | Role::Feed => AtspiRole::Panel,
148 Role::GenericContainer
149 | Role::FooterAsNonLandmark
150 | Role::HeaderAsNonLandmark
151 | Role::Ruby => AtspiRole::Section,
152 Role::GraphicsDocument => AtspiRole::DocumentFrame,
153 Role::GraphicsObject => AtspiRole::Panel,
154 Role::GraphicsSymbol => AtspiRole::Image,
155 Role::Grid => AtspiRole::Table,
156 Role::Group => AtspiRole::Panel,
157 Role::Heading => AtspiRole::Heading,
158 Role::Iframe | Role::IframePresentational => AtspiRole::InternalFrame,
159 Role::Image => AtspiRole::Image,
161 Role::InlineTextBox => AtspiRole::Static,
162 Role::Legend => AtspiRole::Label,
163 Role::LayoutTable => AtspiRole::Section,
165 Role::LayoutTableCell => AtspiRole::Section,
166 Role::LayoutTableRow => AtspiRole::Section,
167 Role::LineBreak => AtspiRole::Static,
170 Role::Link => AtspiRole::Link,
171 Role::List => AtspiRole::List,
172 Role::ListBox => AtspiRole::ListBox,
173 Role::ListBoxOption => AtspiRole::ListItem,
175 Role::ListGrid => AtspiRole::Table,
176 Role::ListItem => AtspiRole::ListItem,
177 Role::ListMarker => AtspiRole::Static,
186 Role::Log => AtspiRole::Log,
187 Role::Main => AtspiRole::Landmark,
188 Role::Mark => AtspiRole::Static,
189 Role::Math => AtspiRole::Math,
190 Role::Marquee => AtspiRole::Marquee,
191 Role::Menu | Role::MenuListPopup => AtspiRole::Menu,
192 Role::MenuBar => AtspiRole::MenuBar,
193 Role::MenuItem | Role::MenuListOption => AtspiRole::MenuItem,
194 Role::MenuItemCheckBox => AtspiRole::CheckMenuItem,
195 Role::MenuItemRadio => AtspiRole::RadioMenuItem,
196 Role::Meter => AtspiRole::LevelBar,
197 Role::Navigation => AtspiRole::Landmark,
198 Role::Note => AtspiRole::Comment,
199 Role::Pane | Role::ScrollView => AtspiRole::Panel,
200 Role::Paragraph => AtspiRole::Paragraph,
201 Role::PdfActionableHighlight => AtspiRole::PushButton,
202 Role::PdfRoot => AtspiRole::DocumentFrame,
203 Role::PluginObject => AtspiRole::Embedded,
204 Role::Portal => AtspiRole::PushButton,
205 Role::Pre => AtspiRole::Section,
206 Role::ProgressIndicator => AtspiRole::ProgressBar,
207 Role::RadioButton => AtspiRole::RadioButton,
208 Role::RadioGroup => AtspiRole::Panel,
209 Role::Region => AtspiRole::Landmark,
210 Role::RootWebArea => AtspiRole::DocumentWeb,
211 Role::Row => AtspiRole::TableRow,
212 Role::RowGroup => AtspiRole::Panel,
213 Role::RowHeader => AtspiRole::RowHeader,
214 Role::RubyAnnotation => AtspiRole::Static,
222 Role::Section => AtspiRole::Section,
223 Role::ScrollBar => AtspiRole::ScrollBar,
224 Role::Search => AtspiRole::Landmark,
225 Role::Slider => AtspiRole::Slider,
226 Role::SpinButton => AtspiRole::SpinButton,
227 Role::Splitter => AtspiRole::Separator,
228 Role::Label => AtspiRole::Label,
229 Role::Status => AtspiRole::StatusBar,
230 Role::SvgRoot => AtspiRole::DocumentFrame,
231 Role::Tab => AtspiRole::PageTab,
232 Role::Table => AtspiRole::Table,
233 Role::TabList => AtspiRole::PageTabList,
234 Role::TabPanel => AtspiRole::ScrollPane,
235 Role::Term => AtspiRole::DescriptionTerm,
238 Role::TitleBar => AtspiRole::TitleBar,
239 Role::TextInput
240 | Role::MultilineTextInput
241 | Role::SearchInput
242 | Role::EmailInput
243 | Role::NumberInput
244 | Role::PhoneNumberInput
245 | Role::UrlInput => AtspiRole::Entry,
246 Role::DateInput
247 | Role::DateTimeInput
248 | Role::WeekInput
249 | Role::MonthInput
250 | Role::TimeInput => AtspiRole::DateEditor,
251 Role::PasswordInput => AtspiRole::PasswordText,
252 Role::Abbr | Role::Code | Role::Emphasis | Role::Strong | Role::Time => {
253 AtspiRole::Static
254 }
255 Role::Timer => AtspiRole::Timer,
256 Role::Toolbar => AtspiRole::ToolBar,
257 Role::Tooltip => AtspiRole::ToolTip,
258 Role::Tree => AtspiRole::Tree,
259 Role::TreeItem => AtspiRole::TreeItem,
260 Role::TreeGrid => AtspiRole::TreeTable,
261 Role::Video => AtspiRole::Video,
262 Role::Window => AtspiRole::Frame,
266 Role::WebView => AtspiRole::Panel,
267 Role::FigureCaption => AtspiRole::Caption,
268 Role::Unknown => AtspiRole::Unknown,
270 Role::ImeCandidate | Role::Keyboard => AtspiRole::RedundantObject,
271 Role::Terminal => AtspiRole::Terminal,
272 }
273 }
274
275 fn is_focused(&self) -> bool {
276 self.0.is_focused()
277 }
278
279 pub(crate) fn state(&self, is_window_focused: bool) -> StateSet {
280 let state = self.0;
281 let atspi_role = self.role();
282 let mut atspi_state = StateSet::empty();
283 if state.parent_id().is_none() && state.role() == Role::Window && is_window_focused {
284 atspi_state.insert(State::Active);
285 }
286 if state.is_text_input() && !state.is_read_only() {
287 atspi_state.insert(State::Editable);
288 }
289 if state.is_focusable() {
291 atspi_state.insert(State::Focusable);
292 }
293 if let Some(orientation) = state.orientation() {
294 atspi_state.insert(if orientation == Orientation::Horizontal {
295 State::Horizontal
296 } else {
297 State::Vertical
298 });
299 }
300 let filter_result = filter(self.0);
301 if filter_result == FilterResult::Include {
302 atspi_state.insert(State::Visible | State::Showing);
303 }
304 if atspi_role != AtspiRole::ToggleButton && state.toggled().is_some() {
305 atspi_state.insert(State::Checkable);
306 }
307 if let Some(selected) = state.is_selected() {
308 if !state.is_disabled() {
309 atspi_state.insert(State::Selectable);
310 }
311 if selected {
312 atspi_state.insert(State::Selected);
313 }
314 }
315 if state.is_text_input() {
316 atspi_state.insert(State::SelectableText);
317 atspi_state.insert(match state.is_multiline() {
318 true => State::MultiLine,
319 false => State::SingleLine,
320 });
321 }
322
323 if state.role() == Role::ProgressIndicator && state.numeric_value().is_none() {
325 atspi_state.insert(State::Indeterminate);
326 }
327
328 match state.toggled() {
330 Some(Toggled::Mixed) => atspi_state.insert(State::Indeterminate),
331 Some(Toggled::True) if atspi_role == AtspiRole::ToggleButton => {
332 atspi_state.insert(State::Pressed)
333 }
334 Some(Toggled::True) => atspi_state.insert(State::Checked),
335 _ => {}
336 }
337
338 if state.is_read_only_supported() && state.is_read_only_or_disabled() {
339 atspi_state.insert(State::ReadOnly);
340 } else {
341 atspi_state.insert(State::Enabled | State::Sensitive);
342 }
343
344 if self.is_focused() {
345 atspi_state.insert(State::Focused);
346 }
347
348 atspi_state
349 }
350
351 fn attributes(&self) -> HashMap<&'static str, String> {
352 let mut attributes = HashMap::new();
353 if let Some(placeholder) = self.0.placeholder() {
354 attributes.insert("placeholder-text", placeholder);
355 }
356 attributes
357 }
358
359 fn is_root(&self) -> bool {
360 self.0.is_root()
361 }
362
363 fn supports_action(&self) -> bool {
364 self.0.default_action_verb().is_some()
365 }
366
367 fn supports_component(&self) -> bool {
368 self.0.raw_bounds().is_some() || self.is_root()
369 }
370
371 fn supports_text(&self) -> bool {
372 self.0.supports_text_ranges()
373 }
374
375 fn supports_value(&self) -> bool {
376 self.current_value().is_some()
377 }
378
379 pub(crate) fn interfaces(&self) -> InterfaceSet {
380 let mut interfaces = InterfaceSet::new(Interface::Accessible);
381 if self.supports_action() {
382 interfaces.insert(Interface::Action);
383 }
384 if self.supports_component() {
385 interfaces.insert(Interface::Component);
386 }
387 if self.supports_text() {
388 interfaces.insert(Interface::Text);
389 }
390 if self.supports_value() {
391 interfaces.insert(Interface::Value);
392 }
393 interfaces
394 }
395
396 pub(crate) fn live(&self) -> AtspiLive {
397 let live = self.0.live();
398 match live {
399 Live::Off => AtspiLive::None,
400 Live::Polite => AtspiLive::Polite,
401 Live::Assertive => AtspiLive::Assertive,
402 }
403 }
404
405 fn n_actions(&self) -> i32 {
406 match self.0.default_action_verb() {
407 Some(_) => 1,
408 None => 0,
409 }
410 }
411
412 fn get_action_name(&self, index: i32) -> String {
413 if index != 0 {
414 return String::new();
415 }
416 String::from(match self.0.default_action_verb() {
417 Some(DefaultActionVerb::Click) => "click",
418 Some(DefaultActionVerb::Focus) => "focus",
419 Some(DefaultActionVerb::Check) => "check",
420 Some(DefaultActionVerb::Uncheck) => "uncheck",
421 Some(DefaultActionVerb::ClickAncestor) => "clickAncestor",
422 Some(DefaultActionVerb::Jump) => "jump",
423 Some(DefaultActionVerb::Open) => "open",
424 Some(DefaultActionVerb::Press) => "press",
425 Some(DefaultActionVerb::Select) => "select",
426 Some(DefaultActionVerb::Unselect) => "unselect",
427 None => "",
428 })
429 }
430
431 fn raw_bounds_and_transform(&self) -> (Option<Rect>, Affine) {
432 let state = self.0;
433 (state.raw_bounds(), state.direct_transform())
434 }
435
436 fn extents(&self, window_bounds: &WindowBounds, coord_type: CoordType) -> Option<Rect> {
437 let mut bounds = self.0.bounding_box();
438 if self.is_root() {
439 let window_bounds = window_bounds.inner.with_origin(Point::ZERO);
440 if !window_bounds.is_empty() {
441 if let Some(bounds) = &mut bounds {
442 bounds.x0 = bounds.x0.min(window_bounds.x1);
443 bounds.y0 = bounds.y0.min(window_bounds.y1);
444 bounds.x1 = bounds.x1.min(window_bounds.x1);
445 bounds.y1 = bounds.y1.min(window_bounds.y1);
446 } else {
447 bounds = Some(window_bounds);
448 }
449 }
450 }
451 bounds.map(|bounds| {
452 let new_origin = window_bounds.accesskit_point_to_atspi_point(
453 bounds.origin(),
454 self.0.filtered_parent(&filter),
455 coord_type,
456 );
457 bounds.with_origin(new_origin)
458 })
459 }
460
461 fn current_value(&self) -> Option<f64> {
462 self.0.numeric_value()
463 }
464
465 pub(crate) fn notify_changes(
466 &self,
467 window_bounds: &WindowBounds,
468 adapter: &Adapter,
469 old: &NodeWrapper<'_>,
470 ) {
471 self.notify_state_changes(adapter, old);
472 self.notify_property_changes(adapter, old);
473 self.notify_bounds_changes(window_bounds, adapter, old);
474 self.notify_children_changes(adapter, old);
475 }
476
477 fn notify_state_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
478 let old_state = old.state(true);
479 let new_state = self.state(true);
480 let changed_states = old_state ^ new_state;
481 for state in changed_states.iter() {
482 if state == State::Focused {
483 continue;
485 }
486 adapter.emit_object_event(
487 self.id(),
488 ObjectEvent::StateChanged(state, new_state.contains(state)),
489 );
490 }
491 }
492
493 fn notify_property_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
494 let name = self.name();
495 if name != old.name() {
496 let name = name.unwrap_or_default();
497 adapter.emit_object_event(
498 self.id(),
499 ObjectEvent::PropertyChanged(Property::Name(name.clone())),
500 );
501
502 let live = self.live();
503 if live != AtspiLive::None {
504 adapter.emit_object_event(self.id(), ObjectEvent::Announcement(name, live));
505 }
506 }
507 let description = self.description();
508 if description != old.description() {
509 adapter.emit_object_event(
510 self.id(),
511 ObjectEvent::PropertyChanged(Property::Description(
512 description.unwrap_or_default(),
513 )),
514 );
515 }
516 let parent_id = self.parent_id();
517 if parent_id != old.parent_id() {
518 let parent = self
519 .0
520 .filtered_parent(&filter)
521 .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
522 adapter.emit_object_event(
523 self.id(),
524 ObjectEvent::PropertyChanged(Property::Parent(parent)),
525 );
526 }
527 let role = self.role();
528 if role != old.role() {
529 adapter.emit_object_event(
530 self.id(),
531 ObjectEvent::PropertyChanged(Property::Role(role)),
532 );
533 }
534 if let Some(value) = self.current_value() {
535 if Some(value) != old.current_value() {
536 adapter.emit_object_event(
537 self.id(),
538 ObjectEvent::PropertyChanged(Property::Value(value)),
539 );
540 }
541 }
542 }
543
544 fn notify_bounds_changes(
545 &self,
546 window_bounds: &WindowBounds,
547 adapter: &Adapter,
548 old: &NodeWrapper<'_>,
549 ) {
550 if self.raw_bounds_and_transform() != old.raw_bounds_and_transform() {
551 if let Some(extents) = self.extents(window_bounds, CoordType::Window) {
552 adapter.emit_object_event(self.id(), ObjectEvent::BoundsChanged(extents.into()));
553 }
554 }
555 }
556
557 fn notify_children_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
558 let old_filtered_children = old.filtered_child_ids().collect::<Vec<NodeId>>();
559 let new_filtered_children = self.filtered_child_ids().collect::<Vec<NodeId>>();
560 for (index, child) in new_filtered_children.iter().enumerate() {
561 if !old_filtered_children.contains(child) {
562 adapter.emit_object_event(self.id(), ObjectEvent::ChildAdded(index, *child));
563 }
564 }
565 for child in old_filtered_children.into_iter() {
566 if !new_filtered_children.contains(&child) {
567 adapter.emit_object_event(self.id(), ObjectEvent::ChildRemoved(child));
568 }
569 }
570 }
571}
572
573#[derive(Clone)]
574pub struct PlatformNode {
575 context: Weak<Context>,
576 adapter_id: usize,
577 id: NodeId,
578}
579
580impl PlatformNode {
581 pub(crate) fn new(context: &Arc<Context>, adapter_id: usize, id: NodeId) -> Self {
582 Self {
583 context: Arc::downgrade(context),
584 adapter_id,
585 id,
586 }
587 }
588
589 fn from_adapter_root(adapter_id_and_context: &(usize, Arc<Context>)) -> Self {
590 let (adapter_id, context) = adapter_id_and_context;
591 Self::new(context, *adapter_id, context.read_tree().state().root_id())
592 }
593
594 fn upgrade_context(&self) -> Result<Arc<Context>> {
595 if let Some(context) = self.context.upgrade() {
596 Ok(context)
597 } else {
598 Err(Error::Defunct)
599 }
600 }
601
602 fn with_tree_state<F, T>(&self, f: F) -> Result<T>
603 where
604 F: FnOnce(&TreeState) -> Result<T>,
605 {
606 let context = self.upgrade_context()?;
607 let tree = context.read_tree();
608 f(tree.state())
609 }
610
611 fn with_tree_state_and_context<F, T>(&self, f: F) -> Result<T>
612 where
613 F: FnOnce(&TreeState, &Context) -> Result<T>,
614 {
615 let context = self.upgrade_context()?;
616 let tree = context.read_tree();
617 f(tree.state(), &context)
618 }
619
620 fn resolve_with_context<F, T>(&self, f: F) -> Result<T>
621 where
622 for<'a> F: FnOnce(Node<'a>, &Context) -> Result<T>,
623 {
624 self.with_tree_state_and_context(|state, context| {
625 if let Some(node) = state.node_by_id(self.id) {
626 f(node, context)
627 } else {
628 Err(Error::Defunct)
629 }
630 })
631 }
632
633 fn resolve_for_text_with_context<F, T>(&self, f: F) -> Result<T>
634 where
635 for<'a> F: FnOnce(Node<'a>, &Context) -> Result<T>,
636 {
637 self.resolve_with_context(|node, context| {
638 let wrapper = NodeWrapper(&node);
639 if wrapper.supports_text() {
640 f(node, context)
641 } else {
642 Err(Error::UnsupportedInterface)
643 }
644 })
645 }
646
647 fn resolve<F, T>(&self, f: F) -> Result<T>
648 where
649 for<'a> F: FnOnce(Node<'a>) -> Result<T>,
650 {
651 self.resolve_with_context(|node, _| f(node))
652 }
653
654 fn resolve_for_text<F, T>(&self, f: F) -> Result<T>
655 where
656 for<'a> F: FnOnce(Node<'a>) -> Result<T>,
657 {
658 self.resolve_for_text_with_context(|node, _| f(node))
659 }
660
661 fn do_action_internal<F>(&self, f: F) -> Result<()>
662 where
663 F: FnOnce(&TreeState, &Context) -> ActionRequest,
664 {
665 let context = self.upgrade_context()?;
666 let tree = context.read_tree();
667 if tree.state().has_node(self.id) {
668 let request = f(tree.state(), &context);
669 drop(tree);
670 context.do_action(request);
671 Ok(())
672 } else {
673 Err(Error::Defunct)
674 }
675 }
676
677 pub fn name(&self) -> Result<String> {
678 self.resolve(|node| {
679 let wrapper = NodeWrapper(&node);
680 Ok(wrapper.name().unwrap_or_default())
681 })
682 }
683
684 pub fn description(&self) -> Result<String> {
685 self.resolve(|node| {
686 let wrapper = NodeWrapper(&node);
687 Ok(wrapper.description().unwrap_or_default())
688 })
689 }
690
691 pub fn relative(&self, id: NodeId) -> Self {
692 Self {
693 context: self.context.clone(),
694 adapter_id: self.adapter_id,
695 id,
696 }
697 }
698
699 pub fn root(&self) -> Result<PlatformRoot> {
700 let context = self.upgrade_context()?;
701 Ok(PlatformRoot::new(&context.app_context))
702 }
703
704 pub fn toolkit_name(&self) -> Result<String> {
705 self.with_tree_state(|state| Ok(state.toolkit_name().unwrap_or_default()))
706 }
707
708 pub fn toolkit_version(&self) -> Result<String> {
709 self.with_tree_state(|state| Ok(state.toolkit_version().unwrap_or_default()))
710 }
711
712 pub fn parent(&self) -> Result<NodeIdOrRoot> {
713 self.resolve(|node| {
714 let parent = node
715 .filtered_parent(&filter)
716 .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
717 Ok(parent)
718 })
719 }
720
721 pub fn child_count(&self) -> Result<i32> {
722 self.resolve(|node| {
723 i32::try_from(node.filtered_children(&filter).count())
724 .map_err(|_| Error::TooManyChildren)
725 })
726 }
727
728 pub fn adapter_id(&self) -> usize {
729 self.adapter_id
730 }
731
732 pub fn id(&self) -> NodeId {
733 self.id
734 }
735
736 pub fn accessible_id(&self) -> Result<String> {
737 self.resolve(|node| {
738 if let Some(author_id) = node.author_id() {
739 Ok(author_id.to_string())
740 } else {
741 Ok(String::new())
742 }
743 })
744 }
745
746 pub fn child_at_index(&self, index: usize) -> Result<Option<NodeId>> {
747 self.resolve(|node| {
748 let child = node
749 .filtered_children(&filter)
750 .nth(index)
751 .map(|child| child.id());
752 Ok(child)
753 })
754 }
755
756 pub fn map_children<T, I>(&self, f: impl Fn(NodeId) -> I) -> Result<T>
757 where
758 T: FromIterator<I>,
759 {
760 self.resolve(|node| {
761 let children = node
762 .filtered_children(&filter)
763 .map(|child| child.id())
764 .map(f)
765 .collect();
766 Ok(children)
767 })
768 }
769
770 pub fn index_in_parent(&self) -> Result<i32> {
771 self.resolve(|node| {
772 i32::try_from(node.preceding_filtered_siblings(&filter).count())
773 .map_err(|_| Error::IndexOutOfRange)
774 })
775 }
776
777 pub fn role(&self) -> Result<AtspiRole> {
778 self.resolve(|node| {
779 let wrapper = NodeWrapper(&node);
780 Ok(wrapper.role())
781 })
782 }
783
784 pub fn localized_role_name(&self) -> Result<String> {
785 self.resolve(|node| Ok(node.role_description().unwrap_or_default()))
786 }
787
788 pub fn state(&self) -> StateSet {
789 self.resolve_with_context(|node, context| {
790 let wrapper = NodeWrapper(&node);
791 Ok(wrapper.state(context.read_tree().state().focus_id().is_some()))
792 })
793 .unwrap_or(State::Defunct.into())
794 }
795
796 pub fn attributes(&self) -> Result<HashMap<&'static str, String>> {
797 self.resolve(|node| {
798 let wrapper = NodeWrapper(&node);
799 Ok(wrapper.attributes())
800 })
801 }
802
803 pub fn supports_action(&self) -> Result<bool> {
804 self.resolve(|node| {
805 let wrapper = NodeWrapper(&node);
806 Ok(wrapper.supports_action())
807 })
808 }
809
810 pub fn supports_component(&self) -> Result<bool> {
811 self.resolve(|node| {
812 let wrapper = NodeWrapper(&node);
813 Ok(wrapper.supports_component())
814 })
815 }
816
817 pub fn supports_text(&self) -> Result<bool> {
818 self.resolve(|node| {
819 let wrapper = NodeWrapper(&node);
820 Ok(wrapper.supports_text())
821 })
822 }
823
824 pub fn supports_value(&self) -> Result<bool> {
825 self.resolve(|node| {
826 let wrapper = NodeWrapper(&node);
827 Ok(wrapper.supports_value())
828 })
829 }
830
831 pub fn interfaces(&self) -> Result<InterfaceSet> {
832 self.resolve(|node| {
833 let wrapper = NodeWrapper(&node);
834 Ok(wrapper.interfaces())
835 })
836 }
837
838 pub fn n_actions(&self) -> Result<i32> {
839 self.resolve(|node| {
840 let wrapper = NodeWrapper(&node);
841 Ok(wrapper.n_actions())
842 })
843 }
844
845 pub fn action_name(&self, index: i32) -> Result<String> {
846 self.resolve(|node| {
847 let wrapper = NodeWrapper(&node);
848 Ok(wrapper.get_action_name(index))
849 })
850 }
851
852 pub fn actions(&self) -> Result<Vec<AtspiAction>> {
853 self.resolve(|node| {
854 let wrapper = NodeWrapper(&node);
855 let n_actions = wrapper.n_actions() as usize;
856 let mut actions = Vec::with_capacity(n_actions);
857 for i in 0..n_actions {
858 actions.push(AtspiAction {
859 localized_name: wrapper.get_action_name(i as i32),
860 description: "".into(),
861 key_binding: "".into(),
862 });
863 }
864 Ok(actions)
865 })
866 }
867
868 pub fn do_action(&self, index: i32) -> Result<bool> {
869 if index != 0 {
870 return Ok(false);
871 }
872 self.do_action_internal(|_, _| ActionRequest {
873 action: Action::Default,
874 target: self.id,
875 data: None,
876 })?;
877 Ok(true)
878 }
879
880 pub fn contains(&self, x: i32, y: i32, coord_type: CoordType) -> Result<bool> {
881 self.resolve_with_context(|node, context| {
882 let window_bounds = context.read_root_window_bounds();
883 let wrapper = NodeWrapper(&node);
884 if let Some(extents) = wrapper.extents(&window_bounds, coord_type) {
885 Ok(extents.contains(Point::new(x.into(), y.into())))
886 } else {
887 Ok(false)
888 }
889 })
890 }
891
892 pub fn accessible_at_point(
893 &self,
894 x: i32,
895 y: i32,
896 coord_type: CoordType,
897 ) -> Result<Option<NodeId>> {
898 self.resolve_with_context(|node, context| {
899 let window_bounds = context.read_root_window_bounds();
900 let point = window_bounds.atspi_point_to_accesskit_point(
901 Point::new(x.into(), y.into()),
902 Some(node),
903 coord_type,
904 );
905 let point = node.transform().inverse() * point;
906 Ok(node.node_at_point(point, &filter).map(|node| node.id()))
907 })
908 }
909
910 pub fn extents(&self, coord_type: CoordType) -> Result<AtspiRect> {
911 self.resolve_with_context(|node, context| {
912 let window_bounds = context.read_root_window_bounds();
913 let wrapper = NodeWrapper(&node);
914 Ok(wrapper
915 .extents(&window_bounds, coord_type)
916 .map_or(AtspiRect::INVALID, AtspiRect::from))
917 })
918 }
919
920 pub fn layer(&self) -> Result<Layer> {
921 self.resolve(|node| {
922 let wrapper = NodeWrapper(&node);
923 if wrapper.role() == AtspiRole::Window {
924 Ok(Layer::Window)
925 } else {
926 Ok(Layer::Widget)
927 }
928 })
929 }
930
931 pub fn grab_focus(&self) -> Result<bool> {
932 self.do_action_internal(|_, _| ActionRequest {
933 action: Action::Focus,
934 target: self.id,
935 data: None,
936 })?;
937 Ok(true)
938 }
939
940 pub fn scroll_to_point(&self, coord_type: CoordType, x: i32, y: i32) -> Result<bool> {
941 self.resolve_with_context(|node, context| {
942 let window_bounds = context.read_root_window_bounds();
943 let point = window_bounds.atspi_point_to_accesskit_point(
944 Point::new(x.into(), y.into()),
945 node.filtered_parent(&filter),
946 coord_type,
947 );
948 context.do_action(ActionRequest {
949 action: Action::ScrollToPoint,
950 target: self.id,
951 data: Some(ActionData::ScrollToPoint(point)),
952 });
953 Ok(())
954 })?;
955 Ok(true)
956 }
957
958 pub fn character_count(&self) -> Result<i32> {
959 self.resolve_for_text(|node| {
960 node.document_range()
961 .end()
962 .to_global_usv_index()
963 .try_into()
964 .map_err(|_| Error::TooManyCharacters)
965 })
966 }
967
968 pub fn caret_offset(&self) -> Result<i32> {
969 self.resolve_for_text(|node| {
970 node.text_selection_focus().map_or(Ok(-1), |focus| {
971 focus
972 .to_global_usv_index()
973 .try_into()
974 .map_err(|_| Error::TooManyCharacters)
975 })
976 })
977 }
978
979 pub fn string_at_offset(
980 &self,
981 offset: i32,
982 granularity: Granularity,
983 ) -> Result<(String, i32, i32)> {
984 self.resolve_for_text(|node| {
985 let range = text_range_from_offset(&node, offset, granularity)?;
986 let text = range.text();
987 let start = range
988 .start()
989 .to_global_usv_index()
990 .try_into()
991 .map_err(|_| Error::TooManyCharacters)?;
992 let end = range
993 .end()
994 .to_global_usv_index()
995 .try_into()
996 .map_err(|_| Error::TooManyCharacters)?;
997
998 Ok((text, start, end))
999 })
1000 }
1001
1002 pub fn text(&self, start_offset: i32, end_offset: i32) -> Result<String> {
1003 self.resolve_for_text(|node| {
1004 let range = text_range_from_offsets(&node, start_offset, end_offset)
1005 .ok_or(Error::IndexOutOfRange)?;
1006 Ok(range.text())
1007 })
1008 }
1009
1010 pub fn set_caret_offset(&self, offset: i32) -> Result<bool> {
1011 self.resolve_for_text_with_context(|node, context| {
1012 let offset = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1013 context.do_action(ActionRequest {
1014 action: Action::SetTextSelection,
1015 target: node.id(),
1016 data: Some(ActionData::SetTextSelection(
1017 offset.to_degenerate_range().to_text_selection(),
1018 )),
1019 });
1020 Ok(true)
1021 })
1022 }
1023
1024 pub fn text_attribute_value(&self, _offset: i32, _attribute_name: &str) -> Result<String> {
1025 Err(Error::UnsupportedInterface)
1027 }
1028
1029 pub fn text_attributes(&self, _offset: i32) -> Result<(HashMap<String, String>, i32, i32)> {
1030 Err(Error::UnsupportedInterface)
1032 }
1033
1034 pub fn default_text_attributes(&self) -> Result<HashMap<String, String>> {
1035 Err(Error::UnsupportedInterface)
1037 }
1038
1039 pub fn character_extents(&self, offset: i32, coord_type: CoordType) -> Result<AtspiRect> {
1040 self.resolve_for_text_with_context(|node, context| {
1041 let range = text_range_from_offset(&node, offset, Granularity::Char)?;
1042 if let Some(bounds) = range.bounding_boxes().first() {
1043 let window_bounds = context.read_root_window_bounds();
1044 let new_origin = window_bounds.accesskit_point_to_atspi_point(
1045 bounds.origin(),
1046 Some(node),
1047 coord_type,
1048 );
1049 Ok(bounds.with_origin(new_origin).into())
1050 } else {
1051 Ok(AtspiRect::INVALID)
1052 }
1053 })
1054 }
1055
1056 pub fn offset_at_point(&self, x: i32, y: i32, coord_type: CoordType) -> Result<i32> {
1057 self.resolve_for_text_with_context(|node, context| {
1058 let window_bounds = context.read_root_window_bounds();
1059 let point = window_bounds.atspi_point_to_accesskit_point(
1060 Point::new(x.into(), y.into()),
1061 Some(node),
1062 coord_type,
1063 );
1064 let point = node.transform().inverse() * point;
1065 node.text_position_at_point(point)
1066 .to_global_usv_index()
1067 .try_into()
1068 .map_err(|_| Error::TooManyCharacters)
1069 })
1070 }
1071
1072 pub fn n_selections(&self) -> Result<i32> {
1073 self.resolve_for_text(|node| {
1074 match node.text_selection().filter(|range| !range.is_degenerate()) {
1075 Some(_) => Ok(1),
1076 None => Ok(0),
1077 }
1078 })
1079 }
1080
1081 pub fn selection(&self, selection_num: i32) -> Result<(i32, i32)> {
1082 if selection_num != 0 {
1083 return Ok((-1, -1));
1084 }
1085
1086 self.resolve_for_text(|node| {
1087 node.text_selection()
1088 .filter(|range| !range.is_degenerate())
1089 .map_or(Ok((-1, -1)), |range| {
1090 let start = range
1091 .start()
1092 .to_global_usv_index()
1093 .try_into()
1094 .map_err(|_| Error::TooManyCharacters)?;
1095 let end = range
1096 .end()
1097 .to_global_usv_index()
1098 .try_into()
1099 .map_err(|_| Error::TooManyCharacters)?;
1100
1101 Ok((start, end))
1102 })
1103 })
1104 }
1105
1106 pub fn add_selection(&self, start_offset: i32, end_offset: i32) -> Result<bool> {
1107 self.set_selection(0, start_offset, end_offset)
1109 }
1110
1111 pub fn remove_selection(&self, selection_num: i32) -> Result<bool> {
1112 if selection_num != 0 {
1113 return Ok(false);
1114 }
1115
1116 self.resolve_for_text_with_context(|node, context| {
1117 let selection_end = node
1120 .text_selection_focus()
1121 .unwrap_or_else(|| node.document_range().start());
1122 context.do_action(ActionRequest {
1123 action: Action::SetTextSelection,
1124 target: node.id(),
1125 data: Some(ActionData::SetTextSelection(
1126 selection_end.to_degenerate_range().to_text_selection(),
1127 )),
1128 });
1129 Ok(true)
1130 })
1131 }
1132
1133 pub fn set_selection(
1134 &self,
1135 selection_num: i32,
1136 start_offset: i32,
1137 end_offset: i32,
1138 ) -> Result<bool> {
1139 if selection_num != 0 {
1140 return Ok(false);
1141 }
1142
1143 self.resolve_for_text_with_context(|node, context| {
1144 let range = text_range_from_offsets(&node, start_offset, end_offset)
1145 .ok_or(Error::IndexOutOfRange)?;
1146 context.do_action(ActionRequest {
1147 action: Action::SetTextSelection,
1148 target: node.id(),
1149 data: Some(ActionData::SetTextSelection(range.to_text_selection())),
1150 });
1151 Ok(true)
1152 })
1153 }
1154
1155 pub fn range_extents(
1156 &self,
1157 start_offset: i32,
1158 end_offset: i32,
1159 coord_type: CoordType,
1160 ) -> Result<AtspiRect> {
1161 self.resolve_for_text_with_context(|node, context| {
1162 if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1163 let window_bounds = context.read_root_window_bounds();
1164 let new_origin = window_bounds.accesskit_point_to_atspi_point(
1165 rect.origin(),
1166 Some(node),
1167 coord_type,
1168 );
1169 Ok(rect.with_origin(new_origin).into())
1170 } else {
1171 Ok(AtspiRect::INVALID)
1172 }
1173 })
1174 }
1175
1176 pub fn text_attribute_run(
1177 &self,
1178 _offset: i32,
1179 _include_defaults: bool,
1180 ) -> Result<(HashMap<String, String>, i32, i32)> {
1181 let character_count = self.character_count()?;
1185 Ok((HashMap::new(), 0, character_count))
1186 }
1187
1188 pub fn scroll_substring_to(
1189 &self,
1190 start_offset: i32,
1191 end_offset: i32,
1192 _: ScrollType,
1193 ) -> Result<bool> {
1194 self.resolve_for_text_with_context(|node, context| {
1195 if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1196 context.do_action(ActionRequest {
1197 action: Action::ScrollIntoView,
1198 target: node.id(),
1199 data: Some(ActionData::ScrollTargetRect(rect)),
1200 });
1201 Ok(true)
1202 } else {
1203 Ok(false)
1204 }
1205 })
1206 }
1207
1208 pub fn scroll_substring_to_point(
1209 &self,
1210 start_offset: i32,
1211 end_offset: i32,
1212 coord_type: CoordType,
1213 x: i32,
1214 y: i32,
1215 ) -> Result<bool> {
1216 self.resolve_for_text_with_context(|node, context| {
1217 let window_bounds = context.read_root_window_bounds();
1218 let target_point = window_bounds.atspi_point_to_accesskit_point(
1219 Point::new(x.into(), y.into()),
1220 Some(node),
1221 coord_type,
1222 );
1223
1224 if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1225 let point = Point::new(target_point.x - rect.x0, target_point.y - rect.y0);
1226 context.do_action(ActionRequest {
1227 action: Action::ScrollToPoint,
1228 target: node.id(),
1229 data: Some(ActionData::ScrollToPoint(point)),
1230 });
1231 return Ok(true);
1232 }
1233 Ok(false)
1234 })
1235 }
1236
1237 pub fn minimum_value(&self) -> Result<f64> {
1238 self.resolve(|node| Ok(node.min_numeric_value().unwrap_or(f64::MIN)))
1239 }
1240
1241 pub fn maximum_value(&self) -> Result<f64> {
1242 self.resolve(|node| Ok(node.max_numeric_value().unwrap_or(f64::MAX)))
1243 }
1244
1245 pub fn minimum_increment(&self) -> Result<f64> {
1246 self.resolve(|node| Ok(node.numeric_value_step().unwrap_or(0.0)))
1247 }
1248
1249 pub fn current_value(&self) -> Result<f64> {
1250 self.resolve(|node| {
1251 let wrapper = NodeWrapper(&node);
1252 Ok(wrapper.current_value().unwrap_or(0.0))
1253 })
1254 }
1255
1256 pub fn set_current_value(&self, value: f64) -> Result<()> {
1257 self.do_action_internal(|_, _| ActionRequest {
1258 action: Action::SetValue,
1259 target: self.id,
1260 data: Some(ActionData::NumericValue(value)),
1261 })
1262 }
1263}
1264
1265impl PartialEq for PlatformNode {
1266 fn eq(&self, other: &Self) -> bool {
1267 self.adapter_id == other.adapter_id && self.id == other.id
1268 }
1269}
1270
1271impl Eq for PlatformNode {}
1272
1273impl Hash for PlatformNode {
1274 fn hash<H: Hasher>(&self, state: &mut H) {
1275 self.adapter_id.hash(state);
1276 self.id.hash(state);
1277 }
1278}
1279
1280#[derive(Clone)]
1281pub struct PlatformRoot {
1282 app_context: Weak<RwLock<AppContext>>,
1283}
1284
1285impl PlatformRoot {
1286 pub fn new(app_context: &Arc<RwLock<AppContext>>) -> Self {
1287 Self {
1288 app_context: Arc::downgrade(app_context),
1289 }
1290 }
1291
1292 fn resolve_app_context<F, T>(&self, f: F) -> Result<T>
1293 where
1294 for<'a> F: FnOnce(RwLockReadGuard<'a, AppContext>) -> Result<T>,
1295 {
1296 let app_context = match self.app_context.upgrade() {
1297 Some(context) => context,
1298 None => return Err(Error::Defunct),
1299 };
1300 let app_context = app_context.read().unwrap();
1301 f(app_context)
1302 }
1303
1304 pub fn name(&self) -> Result<String> {
1305 self.resolve_app_context(|context| Ok(context.name.clone().unwrap_or_default()))
1306 }
1307
1308 pub fn child_count(&self) -> Result<i32> {
1309 self.resolve_app_context(|context| {
1310 i32::try_from(context.adapters.len()).map_err(|_| Error::TooManyChildren)
1311 })
1312 }
1313
1314 pub fn child_at_index(&self, index: usize) -> Result<Option<PlatformNode>> {
1315 self.resolve_app_context(|context| {
1316 let child = context
1317 .adapters
1318 .get(index)
1319 .map(PlatformNode::from_adapter_root);
1320 Ok(child)
1321 })
1322 }
1323
1324 pub fn child_id_at_index(&self, index: usize) -> Result<Option<(usize, NodeId)>> {
1325 self.resolve_app_context(|context| {
1326 let child = context
1327 .adapters
1328 .get(index)
1329 .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()));
1330 Ok(child)
1331 })
1332 }
1333
1334 pub fn map_children<T, I>(&self, f: impl Fn(PlatformNode) -> I) -> Result<T>
1335 where
1336 T: FromIterator<I>,
1337 {
1338 self.resolve_app_context(|context| {
1339 let children = context
1340 .adapters
1341 .iter()
1342 .map(PlatformNode::from_adapter_root)
1343 .map(f)
1344 .collect();
1345 Ok(children)
1346 })
1347 }
1348
1349 pub fn map_child_ids<T, I>(&self, f: impl Fn((usize, NodeId)) -> I) -> Result<T>
1350 where
1351 T: FromIterator<I>,
1352 {
1353 self.resolve_app_context(|context| {
1354 let children = context
1355 .adapters
1356 .iter()
1357 .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()))
1358 .map(f)
1359 .collect();
1360 Ok(children)
1361 })
1362 }
1363
1364 pub fn toolkit_name(&self) -> Result<String> {
1365 self.resolve_app_context(|context| Ok(context.toolkit_name.clone().unwrap_or_default()))
1366 }
1367
1368 pub fn toolkit_version(&self) -> Result<String> {
1369 self.resolve_app_context(|context| Ok(context.toolkit_version.clone().unwrap_or_default()))
1370 }
1371
1372 pub fn id(&self) -> Result<i32> {
1373 self.resolve_app_context(|context| Ok(context.id.unwrap_or(-1)))
1374 }
1375
1376 pub fn set_id(&mut self, id: i32) -> Result<()> {
1377 let app_context = match self.app_context.upgrade() {
1378 Some(context) => context,
1379 None => return Err(Error::Defunct),
1380 };
1381 let mut app_context = app_context.write().unwrap();
1382 app_context.id = Some(id);
1383 Ok(())
1384 }
1385}
1386
1387impl PartialEq for PlatformRoot {
1388 fn eq(&self, other: &Self) -> bool {
1389 self.app_context.ptr_eq(&other.app_context)
1390 }
1391}
1392
1393impl Hash for PlatformRoot {
1394 fn hash<H: Hasher>(&self, state: &mut H) {
1395 self.app_context.as_ptr().hash(state);
1396 }
1397}
1398
1399#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1400pub enum NodeIdOrRoot {
1401 Node(NodeId),
1402 Root,
1403}