1use accesskit::{
12 Action, ActionData, ActionRequest, Affine, Live, NodeId as LocalNodeId, Orientation, Point,
13 Rect, Role, Toggled, TreeId,
14};
15use accesskit_consumer::{FilterResult, Node, NodeId, Tree, TreeState};
16use atspi_common::{
17 CoordType, Granularity, Interface, InterfaceSet, Layer, Politeness, RelationType,
18 Role as AtspiRole, 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 text_attributes::ATTRIBUTE_GETTERS,
32 util::*,
33 Action as AtspiAction, Error, ObjectEvent, Property, Rect as AtspiRect, Result,
34};
35
36pub(crate) struct NodeWrapper<'a>(pub(crate) &'a Node<'a>);
37
38impl NodeWrapper<'_> {
39 pub(crate) fn name(&self) -> Option<String> {
40 if self.0.label_comes_from_value() {
41 self.0.value()
42 } else {
43 self.0.label()
44 }
45 }
46
47 pub(crate) fn description(&self) -> Option<String> {
48 self.0.description()
49 }
50
51 pub(crate) fn parent_id(&self) -> Option<NodeId> {
52 self.0.parent_id()
53 }
54
55 pub(crate) fn id(&self) -> NodeId {
56 self.0.id()
57 }
58
59 fn filtered_child_ids(
60 &self,
61 ) -> impl DoubleEndedIterator<Item = NodeId> + FusedIterator<Item = NodeId> + '_ {
62 self.0.filtered_children(&filter).map(|child| child.id())
63 }
64
65 pub(crate) fn role(&self) -> AtspiRole {
66 if self.0.has_role_description() {
67 return AtspiRole::Extended;
68 }
69
70 match self.0.role() {
71 Role::Alert => AtspiRole::Notification,
72 Role::AlertDialog => AtspiRole::Alert,
73 Role::Comment | Role::Suggestion => AtspiRole::Section,
74 Role::Application => AtspiRole::Embedded,
76 Role::Article => AtspiRole::Article,
77 Role::Audio => AtspiRole::Audio,
78 Role::Banner | Role::Header => AtspiRole::Landmark,
79 Role::Blockquote => AtspiRole::BlockQuote,
80 Role::Caret => AtspiRole::Unknown,
81 Role::Button => {
82 if self.0.toggled().is_some() {
83 AtspiRole::ToggleButton
84 } else {
85 AtspiRole::Button
86 }
87 }
88 Role::DefaultButton => AtspiRole::Button,
89 Role::Canvas => AtspiRole::Canvas,
90 Role::Caption => AtspiRole::Caption,
91 Role::Cell | Role::GridCell => AtspiRole::TableCell,
92 Role::CheckBox => AtspiRole::CheckBox,
93 Role::Switch => AtspiRole::ToggleButton,
94 Role::ColorWell => AtspiRole::Button,
95 Role::ColumnHeader => AtspiRole::ColumnHeader,
96 Role::ComboBox | Role::EditableComboBox => AtspiRole::ComboBox,
97 Role::Complementary => AtspiRole::Landmark,
98 Role::ContentDeletion => AtspiRole::ContentDeletion,
99 Role::ContentInsertion => AtspiRole::ContentInsertion,
100 Role::ContentInfo | Role::Footer => AtspiRole::Landmark,
101 Role::Definition => AtspiRole::DescriptionValue,
102 Role::DescriptionList => AtspiRole::DescriptionList,
103 Role::Details => AtspiRole::Panel,
104 Role::Dialog => AtspiRole::Dialog,
105 Role::DisclosureTriangle => AtspiRole::ToggleButton,
106 Role::DocCover => AtspiRole::Image,
107 Role::DocBackLink | Role::DocBiblioRef | Role::DocGlossRef | Role::DocNoteRef => {
108 AtspiRole::Link
109 }
110 Role::DocBiblioEntry | Role::DocEndnote => AtspiRole::ListItem,
111 Role::DocNotice | Role::DocTip => AtspiRole::Comment,
112 Role::DocFootnote => AtspiRole::Footnote,
113 Role::DocPageBreak => AtspiRole::Separator,
114 Role::DocPageFooter => AtspiRole::Footer,
115 Role::DocPageHeader => AtspiRole::Header,
116 Role::DocAcknowledgements
117 | Role::DocAfterword
118 | Role::DocAppendix
119 | Role::DocBibliography
120 | Role::DocChapter
121 | Role::DocConclusion
122 | Role::DocCredits
123 | Role::DocEndnotes
124 | Role::DocEpilogue
125 | Role::DocErrata
126 | Role::DocForeword
127 | Role::DocGlossary
128 | Role::DocIndex
129 | Role::DocIntroduction
130 | Role::DocPageList
131 | Role::DocPart
132 | Role::DocPreface
133 | Role::DocPrologue
134 | Role::DocToc => AtspiRole::Landmark,
135 Role::DocAbstract
136 | Role::DocColophon
137 | Role::DocCredit
138 | Role::DocDedication
139 | Role::DocEpigraph
140 | Role::DocExample
141 | Role::DocPullquote
142 | Role::DocQna => AtspiRole::Section,
143 Role::DocSubtitle => AtspiRole::Heading,
144 Role::Document => AtspiRole::DocumentFrame,
145 Role::EmbeddedObject => AtspiRole::Embedded,
146 Role::Form => AtspiRole::Form,
150 Role::Figure | Role::Feed => AtspiRole::Panel,
151 Role::GenericContainer | 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::TextRun => 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::Button,
202 Role::PdfRoot => AtspiRole::DocumentFrame,
203 Role::PluginObject => AtspiRole::Embedded,
204 Role::ProgressIndicator => AtspiRole::ProgressBar,
205 Role::RadioButton => AtspiRole::RadioButton,
206 Role::RadioGroup => AtspiRole::Panel,
207 Role::Region => AtspiRole::Landmark,
208 Role::RootWebArea => AtspiRole::DocumentWeb,
209 Role::Row => AtspiRole::TableRow,
210 Role::RowGroup => AtspiRole::Panel,
211 Role::RowHeader => AtspiRole::RowHeader,
212 Role::RubyAnnotation => AtspiRole::Static,
220 Role::Section => AtspiRole::Section,
221 Role::SectionFooter => AtspiRole::Footer,
222 Role::SectionHeader => AtspiRole::Header,
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 is_window_focused
284 && ((state.parent_id().is_none() && state.role() == Role::Window)
285 || (state.is_dialog()
286 && state.tree_state.active_dialog().map(|d| d.id()) == Some(state.id())))
287 {
288 atspi_state.insert(State::Active);
289 }
290 if state.is_text_input() && !state.is_read_only() {
291 atspi_state.insert(State::Editable);
292 }
293 if state.is_focusable(&filter) {
295 atspi_state.insert(State::Focusable);
296 }
297 let filter_result = filter(self.0);
298 if filter_result == FilterResult::Include {
299 atspi_state.insert(State::Visible | State::Showing);
300 }
301 if state.is_required() {
302 atspi_state.insert(State::Required);
303 }
304 if state.is_multiselectable() {
305 atspi_state.insert(State::Multiselectable);
306 }
307 if let Some(orientation) = state.orientation() {
308 atspi_state.insert(if orientation == Orientation::Horizontal {
309 State::Horizontal
310 } else {
311 State::Vertical
312 });
313 }
314 if atspi_role != AtspiRole::ToggleButton && state.toggled().is_some() {
315 atspi_state.insert(State::Checkable);
316 }
317 if state.is_modal() {
318 atspi_state.insert(State::Modal);
319 }
320 if let Some(selected) = state.is_selected() {
321 if !state.is_disabled() {
322 atspi_state.insert(State::Selectable);
323 }
324 if selected {
325 atspi_state.insert(State::Selected);
326 }
327 }
328 if state.is_text_input() {
329 atspi_state.insert(State::SelectableText);
330 atspi_state.insert(match state.is_multiline() {
331 true => State::MultiLine,
332 false => State::SingleLine,
333 });
334 }
335
336 if state.role() == Role::ProgressIndicator && state.numeric_value().is_none() {
338 atspi_state.insert(State::Indeterminate);
339 }
340
341 match state.toggled() {
343 Some(Toggled::Mixed) => atspi_state.insert(State::Indeterminate),
344 Some(Toggled::True) if atspi_role == AtspiRole::ToggleButton => {
345 atspi_state.insert(State::Pressed)
346 }
347 Some(Toggled::True) => atspi_state.insert(State::Checked),
348 _ => {}
349 }
350
351 if state.is_read_only_supported() && state.is_read_only_or_disabled() {
352 atspi_state.insert(State::ReadOnly);
353 } else {
354 atspi_state.insert(State::Enabled | State::Sensitive);
355 }
356
357 if self.is_focused() {
358 atspi_state.insert(State::Focused);
359 }
360
361 atspi_state
362 }
363
364 fn placeholder(&self) -> Option<&str> {
365 self.0.placeholder()
366 }
367
368 fn position_in_set(&self) -> Option<String> {
369 self.0.position_in_set().map(|p| (p + 1).to_string())
370 }
371
372 fn size_of_set(&self) -> Option<String> {
373 self.0
374 .size_of_set_from_container(&filter)
375 .map(|s| s.to_string())
376 }
377
378 fn braille_label(&self) -> Option<&str> {
379 self.0.braille_label()
380 }
381
382 fn braille_role_description(&self) -> Option<&str> {
383 self.0.braille_role_description()
384 }
385
386 fn attributes(&self) -> HashMap<&'static str, String> {
387 let mut attributes = HashMap::new();
388 if let Some(placeholder) = self.placeholder() {
389 attributes.insert("placeholder-text", placeholder.to_string());
390 }
391 if let Some(position_in_set) = self.position_in_set() {
392 attributes.insert("posinset", position_in_set);
393 }
394 if let Some(size_of_set) = self.size_of_set() {
395 attributes.insert("setsize", size_of_set);
396 }
397 if let Some(label) = self.braille_label() {
398 attributes.insert("braillelabel", label.to_string());
399 }
400 if let Some(role_description) = self.braille_role_description() {
401 attributes.insert("brailleroledescription", role_description.to_string());
402 }
403
404 attributes
405 }
406
407 fn is_root(&self) -> bool {
408 self.0.is_root()
409 }
410
411 fn supports_action(&self) -> bool {
412 self.0.is_clickable(&filter)
413 }
414
415 fn supports_component(&self) -> bool {
416 self.0.raw_bounds().is_some() || self.is_root()
417 }
418
419 fn supports_hyperlink(&self) -> bool {
420 self.0.supports_url()
421 }
422
423 fn supports_selection(&self) -> bool {
424 self.0.is_container_with_selectable_children()
425 }
426
427 fn supports_text(&self) -> bool {
428 self.0.supports_text_ranges()
429 }
430
431 fn supports_value(&self) -> bool {
432 self.current_value().is_some()
433 }
434
435 pub(crate) fn interfaces(&self) -> InterfaceSet {
436 let mut interfaces = InterfaceSet::new(Interface::Accessible);
437 if self.supports_action() {
438 interfaces.insert(Interface::Action);
439 }
440 if self.supports_component() {
441 interfaces.insert(Interface::Component);
442 }
443 if self.supports_hyperlink() {
444 interfaces.insert(Interface::Hyperlink);
445 }
446 if self.supports_selection() {
447 interfaces.insert(Interface::Selection);
448 }
449 if self.supports_text() {
450 interfaces.insert(Interface::Text);
451 }
452 if self.supports_value() {
453 interfaces.insert(Interface::Value);
454 }
455 interfaces
456 }
457
458 pub(crate) fn live(&self) -> Politeness {
459 let live = self.0.live();
460 match live {
461 Live::Off => Politeness::None,
462 Live::Polite => Politeness::Polite,
463 Live::Assertive => Politeness::Assertive,
464 }
465 }
466
467 fn n_actions(&self) -> i32 {
468 if self.0.is_clickable(&filter) {
469 1
470 } else {
471 0
472 }
473 }
474
475 fn get_action_name(&self, index: i32) -> String {
476 if index != 0 {
477 return String::new();
478 }
479 String::from(if self.0.is_clickable(&filter) {
480 "click"
481 } else {
482 ""
483 })
484 }
485
486 fn raw_bounds_and_transform(&self) -> (Option<Rect>, Affine) {
487 let state = self.0;
488 (state.raw_bounds(), state.direct_transform())
489 }
490
491 fn extents(&self, window_bounds: &WindowBounds, coord_type: CoordType) -> Option<Rect> {
492 let mut bounds = self.0.bounding_box();
493 if self.is_root() {
494 let window_bounds = window_bounds.inner.with_origin(Point::ZERO);
495 if !window_bounds.is_empty() {
496 if let Some(bounds) = &mut bounds {
497 bounds.x0 = bounds.x0.min(window_bounds.x1);
498 bounds.y0 = bounds.y0.min(window_bounds.y1);
499 bounds.x1 = bounds.x1.min(window_bounds.x1);
500 bounds.y1 = bounds.y1.min(window_bounds.y1);
501 } else {
502 bounds = Some(window_bounds);
503 }
504 }
505 }
506 bounds.map(|bounds| {
507 let new_origin = window_bounds.accesskit_point_to_atspi_point(
508 bounds.origin(),
509 self.0.filtered_parent(&filter),
510 coord_type,
511 );
512 bounds.with_origin(new_origin)
513 })
514 }
515
516 fn current_value(&self) -> Option<f64> {
517 self.0.numeric_value()
518 }
519
520 pub(crate) fn notify_changes(
521 &self,
522 window_bounds: &WindowBounds,
523 adapter: &Adapter,
524 old: &NodeWrapper<'_>,
525 ) {
526 self.notify_state_changes(adapter, old);
527 self.notify_property_changes(adapter, old);
528 self.notify_bounds_changes(window_bounds, adapter, old);
529 self.notify_children_changes(adapter, old);
530 }
531
532 fn notify_state_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
533 let old_state = old.state(true);
534 let new_state = self.state(true);
535 let changed_states = old_state ^ new_state;
536 for state in changed_states.iter() {
537 if state == State::Focused {
538 continue;
540 }
541 adapter.emit_object_event(
542 self.id(),
543 ObjectEvent::StateChanged(state, new_state.contains(state)),
544 );
545 }
546 }
547
548 fn notify_property_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
549 let name = self.name();
550 if name != old.name() {
551 let name = name.unwrap_or_default();
552 adapter.emit_object_event(
553 self.id(),
554 ObjectEvent::PropertyChanged(Property::Name(name.clone())),
555 );
556
557 let live = self.live();
558 if live != Politeness::None {
559 adapter.emit_object_event(self.id(), ObjectEvent::Announcement(name, live));
560 }
561 }
562 let description = self.description();
563 if description != old.description() {
564 adapter.emit_object_event(
565 self.id(),
566 ObjectEvent::PropertyChanged(Property::Description(
567 description.unwrap_or_default(),
568 )),
569 );
570 }
571 let parent_id = self.parent_id();
572 if parent_id != old.parent_id() {
573 let parent = self
574 .0
575 .filtered_parent(&filter)
576 .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
577 adapter.emit_object_event(
578 self.id(),
579 ObjectEvent::PropertyChanged(Property::Parent(parent)),
580 );
581 }
582 let role = self.role();
583 if role != old.role() {
584 adapter.emit_object_event(
585 self.id(),
586 ObjectEvent::PropertyChanged(Property::Role(role)),
587 );
588 }
589 if let Some(value) = self.current_value() {
590 if Some(value) != old.current_value() {
591 adapter.emit_object_event(
592 self.id(),
593 ObjectEvent::PropertyChanged(Property::Value(value)),
594 );
595 }
596 }
597 }
598
599 fn notify_bounds_changes(
600 &self,
601 window_bounds: &WindowBounds,
602 adapter: &Adapter,
603 old: &NodeWrapper<'_>,
604 ) {
605 if self.raw_bounds_and_transform() != old.raw_bounds_and_transform() {
606 if let Some(extents) = self.extents(window_bounds, CoordType::Window) {
607 adapter.emit_object_event(self.id(), ObjectEvent::BoundsChanged(extents.into()));
608 }
609 }
610 }
611
612 fn notify_children_changes(&self, adapter: &Adapter, old: &NodeWrapper<'_>) {
613 let old_filtered_children = old.filtered_child_ids().collect::<Vec<NodeId>>();
614 let new_filtered_children = self.filtered_child_ids().collect::<Vec<NodeId>>();
615 for (index, child) in new_filtered_children.iter().enumerate() {
616 if !old_filtered_children.contains(child) {
617 adapter.emit_object_event(self.id(), ObjectEvent::ChildAdded(index, *child));
618 }
619 }
620 for child in old_filtered_children.into_iter() {
621 if !new_filtered_children.contains(&child) {
622 adapter.emit_object_event(self.id(), ObjectEvent::ChildRemoved(child));
623 }
624 }
625 }
626}
627
628#[derive(Clone)]
629pub struct PlatformNode {
630 context: Weak<Context>,
631 adapter_id: usize,
632 id: NodeId,
633}
634
635impl PlatformNode {
636 pub(crate) fn new(context: &Arc<Context>, adapter_id: usize, id: NodeId) -> Self {
637 Self {
638 context: Arc::downgrade(context),
639 adapter_id,
640 id,
641 }
642 }
643
644 fn from_adapter_root(adapter_id_and_context: &(usize, Arc<Context>)) -> Self {
645 let (adapter_id, context) = adapter_id_and_context;
646 Self::new(context, *adapter_id, context.read_tree().state().root_id())
647 }
648
649 fn upgrade_context(&self) -> Result<Arc<Context>> {
650 if let Some(context) = self.context.upgrade() {
651 Ok(context)
652 } else {
653 Err(Error::Defunct)
654 }
655 }
656
657 fn with_tree_state<F, T>(&self, f: F) -> Result<T>
658 where
659 F: FnOnce(&TreeState) -> Result<T>,
660 {
661 let context = self.upgrade_context()?;
662 let tree = context.read_tree();
663 f(tree.state())
664 }
665
666 fn with_tree_and_context<F, T>(&self, f: F) -> Result<T>
667 where
668 F: FnOnce(&Tree, &Context) -> Result<T>,
669 {
670 let context = self.upgrade_context()?;
671 let tree = context.read_tree();
672 f(&tree, &context)
673 }
674
675 fn resolve_with_context<F, T>(&self, f: F) -> Result<T>
676 where
677 for<'a> F: FnOnce(Node<'a>, &'a Tree, &Context) -> Result<T>,
678 {
679 self.with_tree_and_context(|tree, context| {
680 if let Some(node) = tree.state().node_by_id(self.id) {
681 f(node, tree, context)
682 } else {
683 Err(Error::Defunct)
684 }
685 })
686 }
687
688 fn resolve_for_selection_with_context<F, T>(&self, f: F) -> Result<T>
689 where
690 for<'a> F: FnOnce(Node<'a>, &'a Tree, &Context) -> Result<T>,
691 {
692 self.resolve_with_context(|node, tree, context| {
693 let wrapper = NodeWrapper(&node);
694 if wrapper.supports_selection() {
695 f(node, tree, context)
696 } else {
697 Err(Error::UnsupportedInterface)
698 }
699 })
700 }
701
702 fn resolve_for_text_with_context<F, T>(&self, f: F) -> Result<T>
703 where
704 for<'a> F: FnOnce(Node<'a>, &'a Tree, &Context) -> Result<T>,
705 {
706 self.resolve_with_context(|node, tree, context| {
707 let wrapper = NodeWrapper(&node);
708 if wrapper.supports_text() {
709 f(node, tree, context)
710 } else {
711 Err(Error::UnsupportedInterface)
712 }
713 })
714 }
715
716 fn resolve<F, T>(&self, f: F) -> Result<T>
717 where
718 for<'a> F: FnOnce(Node<'a>) -> Result<T>,
719 {
720 self.resolve_with_context(|node, _, _| f(node))
721 }
722
723 fn resolve_for_selection<F, T>(&self, f: F) -> Result<T>
724 where
725 for<'a> F: FnOnce(Node<'a>) -> Result<T>,
726 {
727 self.resolve(|node| {
728 let wrapper = NodeWrapper(&node);
729 if wrapper.supports_selection() {
730 f(node)
731 } else {
732 Err(Error::UnsupportedInterface)
733 }
734 })
735 }
736
737 fn resolve_for_text<F, T>(&self, f: F) -> Result<T>
738 where
739 for<'a> F: FnOnce(Node<'a>) -> Result<T>,
740 {
741 self.resolve_for_text_with_context(|node, _, _| f(node))
742 }
743
744 fn do_action_internal<F>(&self, target: NodeId, f: F) -> Result<()>
745 where
746 F: FnOnce(&TreeState, &Context, LocalNodeId, TreeId) -> ActionRequest,
747 {
748 let context = self.upgrade_context()?;
749 let tree = context.read_tree();
750 if let Some((target_node, target_tree)) = tree.state().locate_node(target) {
751 let request = f(tree.state(), &context, target_node, target_tree);
752 drop(tree);
753 context.do_action(request);
754 Ok(())
755 } else {
756 Err(Error::Defunct)
757 }
758 }
759
760 pub fn name(&self) -> Result<String> {
761 self.resolve(|node| {
762 let wrapper = NodeWrapper(&node);
763 Ok(wrapper.name().unwrap_or_default())
764 })
765 }
766
767 pub fn description(&self) -> Result<String> {
768 self.resolve(|node| {
769 let wrapper = NodeWrapper(&node);
770 Ok(wrapper.description().unwrap_or_default())
771 })
772 }
773
774 pub fn relative(&self, id: NodeId) -> Self {
775 Self {
776 context: self.context.clone(),
777 adapter_id: self.adapter_id,
778 id,
779 }
780 }
781
782 pub fn root(&self) -> Result<PlatformRoot> {
783 let context = self.upgrade_context()?;
784 Ok(PlatformRoot::new(&context.app_context))
785 }
786
787 pub fn toolkit_name(&self) -> Result<String> {
788 self.with_tree_state(|state| Ok(state.toolkit_name().unwrap_or_default().to_string()))
789 }
790
791 pub fn toolkit_version(&self) -> Result<String> {
792 self.with_tree_state(|state| Ok(state.toolkit_version().unwrap_or_default().to_string()))
793 }
794
795 pub fn parent(&self) -> Result<NodeIdOrRoot> {
796 self.resolve(|node| {
797 let parent = node
798 .filtered_parent(&filter)
799 .map_or(NodeIdOrRoot::Root, |node| NodeIdOrRoot::Node(node.id()));
800 Ok(parent)
801 })
802 }
803
804 pub fn child_count(&self) -> Result<i32> {
805 self.resolve(|node| {
806 i32::try_from(node.filtered_children(&filter).count())
807 .map_err(|_| Error::TooManyChildren)
808 })
809 }
810
811 pub fn adapter_id(&self) -> usize {
812 self.adapter_id
813 }
814
815 pub fn id(&self) -> NodeId {
816 self.id
817 }
818
819 pub fn accessible_id(&self) -> Result<String> {
820 self.resolve(|node| {
821 if let Some(author_id) = node.author_id() {
822 Ok(author_id.to_string())
823 } else {
824 Ok(String::new())
825 }
826 })
827 }
828
829 pub fn child_at_index(&self, index: usize) -> Result<Option<NodeId>> {
830 self.resolve(|node| {
831 let child = node
832 .filtered_children(&filter)
833 .nth(index)
834 .map(|child| child.id());
835 Ok(child)
836 })
837 }
838
839 pub fn map_children<T, I>(&self, f: impl Fn(NodeId) -> I) -> Result<T>
840 where
841 T: FromIterator<I>,
842 {
843 self.resolve(|node| {
844 let children = node
845 .filtered_children(&filter)
846 .map(|child| child.id())
847 .map(f)
848 .collect();
849 Ok(children)
850 })
851 }
852
853 pub fn index_in_parent(&self) -> Result<i32> {
854 self.resolve(|node| {
855 i32::try_from(node.preceding_filtered_siblings(&filter).count())
856 .map_err(|_| Error::IndexOutOfRange)
857 })
858 }
859
860 pub fn relation_set<T>(
861 &self,
862 f: impl Fn(NodeId) -> T,
863 ) -> Result<HashMap<RelationType, Vec<T>>> {
864 self.resolve(|node| {
865 let mut relations = HashMap::new();
866 let controls: Vec<_> = node
867 .controls()
868 .filter(|controlled| filter(controlled) == FilterResult::Include)
869 .map(|controlled| controlled.id())
870 .map(f)
871 .collect();
872 if !controls.is_empty() {
873 relations.insert(RelationType::ControllerFor, controls);
874 }
875 Ok(relations)
876 })
877 }
878
879 pub fn role(&self) -> Result<AtspiRole> {
880 self.resolve(|node| {
881 let wrapper = NodeWrapper(&node);
882 Ok(wrapper.role())
883 })
884 }
885
886 pub fn localized_role_name(&self) -> Result<String> {
887 self.resolve(|node| Ok(node.role_description().unwrap_or_default().to_string()))
888 }
889
890 pub fn state(&self) -> StateSet {
891 self.resolve_with_context(|node, tree, _| {
892 let wrapper = NodeWrapper(&node);
893 Ok(wrapper.state(tree.state().focus_id().is_some()))
894 })
895 .unwrap_or(State::Defunct.into())
896 }
897
898 pub fn attributes(&self) -> Result<HashMap<&'static str, String>> {
899 self.resolve(|node| {
900 let wrapper = NodeWrapper(&node);
901 Ok(wrapper.attributes())
902 })
903 }
904
905 pub fn supports_action(&self) -> Result<bool> {
906 self.resolve(|node| {
907 let wrapper = NodeWrapper(&node);
908 Ok(wrapper.supports_action())
909 })
910 }
911
912 pub fn supports_component(&self) -> Result<bool> {
913 self.resolve(|node| {
914 let wrapper = NodeWrapper(&node);
915 Ok(wrapper.supports_component())
916 })
917 }
918
919 pub fn supports_hyperlink(&self) -> Result<bool> {
920 self.resolve(|node| {
921 let wrapper = NodeWrapper(&node);
922 Ok(wrapper.supports_hyperlink())
923 })
924 }
925
926 pub fn supports_selection(&self) -> Result<bool> {
927 self.resolve(|node| {
928 let wrapper = NodeWrapper(&node);
929 Ok(wrapper.supports_selection())
930 })
931 }
932
933 pub fn supports_text(&self) -> Result<bool> {
934 self.resolve(|node| {
935 let wrapper = NodeWrapper(&node);
936 Ok(wrapper.supports_text())
937 })
938 }
939
940 pub fn supports_value(&self) -> Result<bool> {
941 self.resolve(|node| {
942 let wrapper = NodeWrapper(&node);
943 Ok(wrapper.supports_value())
944 })
945 }
946
947 pub fn interfaces(&self) -> Result<InterfaceSet> {
948 self.resolve(|node| {
949 let wrapper = NodeWrapper(&node);
950 Ok(wrapper.interfaces())
951 })
952 }
953
954 pub fn n_actions(&self) -> Result<i32> {
955 self.resolve(|node| {
956 let wrapper = NodeWrapper(&node);
957 Ok(wrapper.n_actions())
958 })
959 }
960
961 pub fn action_name(&self, index: i32) -> Result<String> {
962 self.resolve(|node| {
963 let wrapper = NodeWrapper(&node);
964 Ok(wrapper.get_action_name(index))
965 })
966 }
967
968 pub fn actions(&self) -> Result<Vec<AtspiAction>> {
969 self.resolve(|node| {
970 let wrapper = NodeWrapper(&node);
971 let n_actions = wrapper.n_actions() as usize;
972 let mut actions = Vec::with_capacity(n_actions);
973 for i in 0..n_actions {
974 actions.push(AtspiAction {
975 localized_name: wrapper.get_action_name(i as i32),
976 description: "".into(),
977 key_binding: "".into(),
978 });
979 }
980 Ok(actions)
981 })
982 }
983
984 pub fn do_action(&self, index: i32) -> Result<bool> {
985 if index != 0 {
986 return Ok(false);
987 }
988 self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
989 action: Action::Click,
990 target_tree,
991 target_node,
992 data: None,
993 })?;
994 Ok(true)
995 }
996
997 pub fn contains(&self, x: i32, y: i32, coord_type: CoordType) -> Result<bool> {
998 self.resolve_with_context(|node, _, context| {
999 let window_bounds = context.read_root_window_bounds();
1000 let wrapper = NodeWrapper(&node);
1001 if let Some(extents) = wrapper.extents(&window_bounds, coord_type) {
1002 Ok(extents.contains(Point::new(x.into(), y.into())))
1003 } else {
1004 Ok(false)
1005 }
1006 })
1007 }
1008
1009 pub fn accessible_at_point(
1010 &self,
1011 x: i32,
1012 y: i32,
1013 coord_type: CoordType,
1014 ) -> Result<Option<NodeId>> {
1015 self.resolve_with_context(|node, _, context| {
1016 let window_bounds = context.read_root_window_bounds();
1017 let point = window_bounds.atspi_point_to_accesskit_point(
1018 Point::new(x.into(), y.into()),
1019 Some(node),
1020 coord_type,
1021 );
1022 let point = node.transform().inverse() * point;
1023 Ok(node.node_at_point(point, &filter).map(|node| node.id()))
1024 })
1025 }
1026
1027 pub fn extents(&self, coord_type: CoordType) -> Result<AtspiRect> {
1028 self.resolve_with_context(|node, _, context| {
1029 let window_bounds = context.read_root_window_bounds();
1030 let wrapper = NodeWrapper(&node);
1031 Ok(wrapper
1032 .extents(&window_bounds, coord_type)
1033 .map_or(AtspiRect::INVALID, AtspiRect::from))
1034 })
1035 }
1036
1037 pub fn layer(&self) -> Result<Layer> {
1038 self.resolve(|node| {
1039 let wrapper = NodeWrapper(&node);
1040 if wrapper.role() == AtspiRole::Window {
1041 Ok(Layer::Window)
1042 } else {
1043 Ok(Layer::Widget)
1044 }
1045 })
1046 }
1047
1048 pub fn grab_focus(&self) -> Result<bool> {
1049 self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
1050 action: Action::Focus,
1051 target_tree,
1052 target_node,
1053 data: None,
1054 })?;
1055 Ok(true)
1056 }
1057
1058 pub fn scroll_to(&self, scroll_type: ScrollType) -> Result<bool> {
1059 self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
1060 action: Action::ScrollIntoView,
1061 target_tree,
1062 target_node,
1063 data: atspi_scroll_type_to_scroll_hint(scroll_type).map(ActionData::ScrollHint),
1064 })?;
1065 Ok(true)
1066 }
1067
1068 pub fn scroll_to_point(&self, coord_type: CoordType, x: i32, y: i32) -> Result<bool> {
1069 self.resolve_with_context(|node, tree, context| {
1070 let window_bounds = context.read_root_window_bounds();
1071 let point = window_bounds.atspi_point_to_accesskit_point(
1072 Point::new(x.into(), y.into()),
1073 node.filtered_parent(&filter),
1074 coord_type,
1075 );
1076 let (target_node, target_tree) =
1077 tree.state().locate_node(self.id).ok_or(Error::Defunct)?;
1078 context.do_action(ActionRequest {
1079 action: Action::ScrollToPoint,
1080 target_tree,
1081 target_node,
1082 data: Some(ActionData::ScrollToPoint(point)),
1083 });
1084 Ok(())
1085 })?;
1086 Ok(true)
1087 }
1088
1089 pub fn n_anchors(&self) -> Result<i32> {
1090 self.resolve(|node| if node.url().is_some() { Ok(1) } else { Ok(0) })
1091 }
1092
1093 pub fn hyperlink_start_index(&self) -> Result<i32> {
1094 self.resolve(|_| {
1095 Ok(-1)
1097 })
1098 }
1099
1100 pub fn hyperlink_end_index(&self) -> Result<i32> {
1101 self.resolve(|_| {
1102 Ok(-1)
1104 })
1105 }
1106
1107 pub fn hyperlink_object(&self, index: i32) -> Result<Option<NodeId>> {
1108 self.resolve(|_| {
1109 if index == 0 {
1110 Ok(Some(self.id))
1111 } else {
1112 Ok(None)
1113 }
1114 })
1115 }
1116
1117 pub fn uri(&self, index: i32) -> Result<String> {
1118 self.resolve(|node| {
1119 if index == 0 {
1120 Ok(node.url().map(|s| s.to_string()).unwrap_or_default())
1121 } else {
1122 Ok(String::new())
1123 }
1124 })
1125 }
1126
1127 pub fn hyperlink_is_valid(&self) -> Result<bool> {
1128 self.resolve(|node| Ok(node.url().is_some()))
1129 }
1130
1131 pub fn n_selected_children(&self) -> Result<i32> {
1132 self.resolve_for_selection(|node| {
1133 node.items(filter)
1134 .filter(|item| item.is_selected() == Some(true))
1135 .count()
1136 .try_into()
1137 .map_err(|_| Error::TooManyChildren)
1138 })
1139 }
1140
1141 pub fn selected_child(&self, selected_child_index: usize) -> Result<Option<NodeId>> {
1142 self.resolve_for_selection(|node| {
1143 Ok(node
1144 .items(filter)
1145 .filter(|item| item.is_selected() == Some(true))
1146 .nth(selected_child_index)
1147 .map(|node| node.id()))
1148 })
1149 }
1150
1151 pub fn select_child(&self, child_index: usize) -> Result<bool> {
1152 self.resolve_for_selection_with_context(|node, tree, context| {
1153 if let Some(child) = node.filtered_children(filter).nth(child_index) {
1154 if let Some(true) = child.is_selected() {
1155 Ok(true)
1156 } else if child.is_selectable() && child.is_clickable(&filter) {
1157 let (target_node, target_tree) =
1158 tree.state().locate_node(child.id()).ok_or(Error::Defunct)?;
1159 context.do_action(ActionRequest {
1160 action: Action::Click,
1161 target_tree,
1162 target_node,
1163 data: None,
1164 });
1165 Ok(true)
1166 } else {
1167 Ok(false)
1168 }
1169 } else {
1170 Err(Error::Defunct)
1171 }
1172 })
1173 }
1174
1175 pub fn deselect_selected_child(&self, selected_child_index: usize) -> Result<bool> {
1176 self.resolve_for_selection_with_context(|node, tree, context| {
1177 if let Some(child) = node
1178 .items(filter)
1179 .filter(|c| c.is_selected() == Some(true))
1180 .nth(selected_child_index)
1181 {
1182 if child.is_clickable(&filter) {
1183 let (target_node, target_tree) =
1184 tree.state().locate_node(child.id()).ok_or(Error::Defunct)?;
1185 context.do_action(ActionRequest {
1186 action: Action::Click,
1187 target_tree,
1188 target_node,
1189 data: None,
1190 });
1191 Ok(true)
1192 } else {
1193 Ok(false)
1194 }
1195 } else {
1196 Err(Error::Defunct)
1197 }
1198 })
1199 }
1200
1201 pub fn is_child_selected(&self, child_index: usize) -> Result<bool> {
1202 self.resolve_for_selection(|node| {
1203 node.filtered_children(filter)
1204 .nth(child_index)
1205 .map(|child| child.is_item_like() && child.is_selected() == Some(true))
1206 .ok_or(Error::Defunct)
1207 })
1208 }
1209
1210 pub fn select_all(&self) -> Result<bool> {
1211 Ok(false)
1213 }
1214
1215 pub fn clear_selection(&self) -> Result<bool> {
1216 Ok(false)
1218 }
1219
1220 pub fn deselect_child(&self, child_index: usize) -> Result<bool> {
1221 self.resolve_for_selection_with_context(|node, tree, context| {
1222 if let Some(child) = node.filtered_children(filter).nth(child_index) {
1223 if let Some(false) = child.is_selected() {
1224 Ok(true)
1225 } else if child.is_selectable() && child.is_clickable(&filter) {
1226 let (target_node, target_tree) =
1227 tree.state().locate_node(child.id()).ok_or(Error::Defunct)?;
1228 context.do_action(ActionRequest {
1229 action: Action::Click,
1230 target_tree,
1231 target_node,
1232 data: None,
1233 });
1234 Ok(true)
1235 } else {
1236 Ok(false)
1237 }
1238 } else {
1239 Err(Error::Defunct)
1240 }
1241 })
1242 }
1243
1244 pub fn character_count(&self) -> Result<i32> {
1245 self.resolve_for_text(|node| {
1246 node.document_range()
1247 .end()
1248 .to_global_usv_index()
1249 .try_into()
1250 .map_err(|_| Error::TooManyCharacters)
1251 })
1252 }
1253
1254 pub fn caret_offset(&self) -> Result<i32> {
1255 self.resolve_for_text(|node| {
1256 node.text_selection_focus().map_or(Ok(-1), |focus| {
1257 focus
1258 .to_global_usv_index()
1259 .try_into()
1260 .map_err(|_| Error::TooManyCharacters)
1261 })
1262 })
1263 }
1264
1265 pub fn string_at_offset(
1266 &self,
1267 offset: i32,
1268 granularity: Granularity,
1269 ) -> Result<(String, i32, i32)> {
1270 self.resolve_for_text(|node| {
1271 let range = text_range_from_offset(&node, offset, granularity)?;
1272 let text = range.text();
1273 let start = range
1274 .start()
1275 .to_global_usv_index()
1276 .try_into()
1277 .map_err(|_| Error::TooManyCharacters)?;
1278 let end = range
1279 .end()
1280 .to_global_usv_index()
1281 .try_into()
1282 .map_err(|_| Error::TooManyCharacters)?;
1283
1284 Ok((text, start, end))
1285 })
1286 }
1287
1288 pub fn text(&self, start_offset: i32, end_offset: i32) -> Result<String> {
1289 self.resolve_for_text(|node| {
1290 let range = text_range_from_offsets(&node, start_offset, end_offset)
1291 .ok_or(Error::IndexOutOfRange)?;
1292 Ok(range.text())
1293 })
1294 }
1295
1296 pub fn set_caret_offset(&self, offset: i32) -> Result<bool> {
1297 self.resolve_for_text_with_context(|node, tree, context| {
1298 let offset = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1299 let (target_node, target_tree) =
1300 tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1301 context.do_action(ActionRequest {
1302 action: Action::SetTextSelection,
1303 target_tree,
1304 target_node,
1305 data: Some(ActionData::SetTextSelection(
1306 offset.to_degenerate_range().to_text_selection(),
1307 )),
1308 });
1309 Ok(true)
1310 })
1311 }
1312
1313 pub fn text_attribute_value(&self, offset: i32, attribute_name: &str) -> Result<String> {
1314 self.resolve_for_text(|node| {
1315 let pos = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1316 Ok(ATTRIBUTE_GETTERS
1317 .get(attribute_name)
1318 .and_then(|getter| (*getter)(pos.inner_node()))
1319 .unwrap_or_default())
1320 })
1321 }
1322
1323 pub fn text_attributes(
1324 &self,
1325 offset: i32,
1326 ) -> Result<(HashMap<&'static str, String>, i32, i32)> {
1327 self.text_attribute_run(offset, false)
1328 }
1329
1330 pub fn default_text_attributes(&self) -> Result<HashMap<&'static str, String>> {
1331 self.resolve_for_text(|node| {
1332 let mut result = HashMap::with_capacity(ATTRIBUTE_GETTERS.len());
1333 for (name, getter) in ATTRIBUTE_GETTERS.entries() {
1334 if let Some(value) = (*getter)(&node) {
1335 result.insert(*name, value);
1336 }
1337 }
1338 Ok(result)
1339 })
1340 }
1341
1342 pub fn character_extents(&self, offset: i32, coord_type: CoordType) -> Result<AtspiRect> {
1343 self.resolve_for_text_with_context(|node, _, context| {
1344 let range = text_range_from_offset(&node, offset, Granularity::Char)?;
1345 if let Some(bounds) = range.bounding_boxes().first() {
1346 let window_bounds = context.read_root_window_bounds();
1347 let new_origin = window_bounds.accesskit_point_to_atspi_point(
1348 bounds.origin(),
1349 Some(node),
1350 coord_type,
1351 );
1352 Ok(bounds.with_origin(new_origin).into())
1353 } else {
1354 Ok(AtspiRect::INVALID)
1355 }
1356 })
1357 }
1358
1359 pub fn offset_at_point(&self, x: i32, y: i32, coord_type: CoordType) -> Result<i32> {
1360 self.resolve_for_text_with_context(|node, _, context| {
1361 let window_bounds = context.read_root_window_bounds();
1362 let point = window_bounds.atspi_point_to_accesskit_point(
1363 Point::new(x.into(), y.into()),
1364 Some(node),
1365 coord_type,
1366 );
1367 let point = node.transform().inverse() * point;
1368 node.text_position_at_point(point)
1369 .to_global_usv_index()
1370 .try_into()
1371 .map_err(|_| Error::TooManyCharacters)
1372 })
1373 }
1374
1375 pub fn n_selections(&self) -> Result<i32> {
1376 self.resolve_for_text(|node| {
1377 match node.text_selection().filter(|range| !range.is_degenerate()) {
1378 Some(_) => Ok(1),
1379 None => Ok(0),
1380 }
1381 })
1382 }
1383
1384 pub fn selection(&self, selection_num: i32) -> Result<(i32, i32)> {
1385 if selection_num != 0 {
1386 return Ok((-1, -1));
1387 }
1388
1389 self.resolve_for_text(|node| {
1390 node.text_selection()
1391 .filter(|range| !range.is_degenerate())
1392 .map_or(Ok((-1, -1)), |range| {
1393 let start = range
1394 .start()
1395 .to_global_usv_index()
1396 .try_into()
1397 .map_err(|_| Error::TooManyCharacters)?;
1398 let end = range
1399 .end()
1400 .to_global_usv_index()
1401 .try_into()
1402 .map_err(|_| Error::TooManyCharacters)?;
1403
1404 Ok((start, end))
1405 })
1406 })
1407 }
1408
1409 pub fn add_selection(&self, start_offset: i32, end_offset: i32) -> Result<bool> {
1410 self.set_selection(0, start_offset, end_offset)
1412 }
1413
1414 pub fn remove_selection(&self, selection_num: i32) -> Result<bool> {
1415 if selection_num != 0 {
1416 return Ok(false);
1417 }
1418
1419 self.resolve_for_text_with_context(|node, tree, context| {
1420 let selection_end = node
1423 .text_selection_focus()
1424 .unwrap_or_else(|| node.document_range().start());
1425 let (target_node, target_tree) =
1426 tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1427 context.do_action(ActionRequest {
1428 action: Action::SetTextSelection,
1429 target_tree,
1430 target_node,
1431 data: Some(ActionData::SetTextSelection(
1432 selection_end.to_degenerate_range().to_text_selection(),
1433 )),
1434 });
1435 Ok(true)
1436 })
1437 }
1438
1439 pub fn set_selection(
1440 &self,
1441 selection_num: i32,
1442 start_offset: i32,
1443 end_offset: i32,
1444 ) -> Result<bool> {
1445 if selection_num != 0 {
1446 return Ok(false);
1447 }
1448
1449 self.resolve_for_text_with_context(|node, tree, context| {
1450 let range = text_range_from_offsets(&node, start_offset, end_offset)
1451 .ok_or(Error::IndexOutOfRange)?;
1452 let (target_node, target_tree) =
1453 tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1454 context.do_action(ActionRequest {
1455 action: Action::SetTextSelection,
1456 target_tree,
1457 target_node,
1458 data: Some(ActionData::SetTextSelection(range.to_text_selection())),
1459 });
1460 Ok(true)
1461 })
1462 }
1463
1464 pub fn range_extents(
1465 &self,
1466 start_offset: i32,
1467 end_offset: i32,
1468 coord_type: CoordType,
1469 ) -> Result<AtspiRect> {
1470 self.resolve_for_text_with_context(|node, _, context| {
1471 if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1472 let window_bounds = context.read_root_window_bounds();
1473 let new_origin = window_bounds.accesskit_point_to_atspi_point(
1474 rect.origin(),
1475 Some(node),
1476 coord_type,
1477 );
1478 Ok(rect.with_origin(new_origin).into())
1479 } else {
1480 Ok(AtspiRect::INVALID)
1481 }
1482 })
1483 }
1484
1485 pub fn text_attribute_run(
1486 &self,
1487 offset: i32,
1488 include_defaults: bool,
1489 ) -> Result<(HashMap<&'static str, String>, i32, i32)> {
1490 self.resolve_for_text(|node| {
1491 let pos = text_position_from_offset(&node, offset).ok_or(Error::IndexOutOfRange)?;
1492 let mut result = HashMap::with_capacity(ATTRIBUTE_GETTERS.len());
1493 for (name, getter) in ATTRIBUTE_GETTERS.entries() {
1494 if let Some(value) = (*getter)(pos.inner_node()) {
1495 if !include_defaults {
1496 if let Some(default) = (*getter)(&node) {
1497 if value == default {
1498 continue;
1499 }
1500 }
1501 }
1502 result.insert(*name, value);
1503 }
1504 }
1505 let start = if pos.is_format_start() {
1506 pos
1507 } else {
1508 pos.backward_to_format_start()
1509 };
1510 let end = pos.forward_to_format_end();
1511 Ok((
1512 result,
1513 start
1514 .to_global_usv_index()
1515 .try_into()
1516 .map_err(|_| Error::TooManyCharacters)?,
1517 end.to_global_usv_index()
1518 .try_into()
1519 .map_err(|_| Error::TooManyCharacters)?,
1520 ))
1521 })
1522 }
1523
1524 pub fn scroll_substring_to(
1525 &self,
1526 start_offset: i32,
1527 end_offset: i32,
1528 scroll_type: ScrollType,
1529 ) -> Result<bool> {
1530 self.resolve_for_text_with_context(|node, tree, context| {
1531 if let Some(range) = text_range_from_offsets(&node, start_offset, end_offset) {
1532 let position = if matches!(
1533 scroll_type,
1534 ScrollType::BottomRight | ScrollType::BottomEdge | ScrollType::RightEdge
1535 ) {
1536 range.end()
1537 } else {
1538 range.start()
1539 };
1540 let (target_node, target_tree) = tree
1541 .state()
1542 .locate_node(position.inner_node().id())
1543 .ok_or(Error::Defunct)?;
1544 context.do_action(ActionRequest {
1545 action: Action::ScrollIntoView,
1546 target_tree,
1547 target_node,
1548 data: atspi_scroll_type_to_scroll_hint(scroll_type).map(ActionData::ScrollHint),
1549 });
1550 Ok(true)
1551 } else {
1552 Ok(false)
1553 }
1554 })
1555 }
1556
1557 pub fn scroll_substring_to_point(
1558 &self,
1559 start_offset: i32,
1560 end_offset: i32,
1561 coord_type: CoordType,
1562 x: i32,
1563 y: i32,
1564 ) -> Result<bool> {
1565 self.resolve_for_text_with_context(|node, tree, context| {
1566 let window_bounds = context.read_root_window_bounds();
1567 let target_point = window_bounds.atspi_point_to_accesskit_point(
1568 Point::new(x.into(), y.into()),
1569 Some(node),
1570 coord_type,
1571 );
1572
1573 if let Some(rect) = text_range_bounds_from_offsets(&node, start_offset, end_offset) {
1574 let point = Point::new(target_point.x - rect.x0, target_point.y - rect.y0);
1575 let (target_node, target_tree) =
1576 tree.state().locate_node(node.id()).ok_or(Error::Defunct)?;
1577 context.do_action(ActionRequest {
1578 action: Action::ScrollToPoint,
1579 target_tree,
1580 target_node,
1581 data: Some(ActionData::ScrollToPoint(point)),
1582 });
1583 return Ok(true);
1584 }
1585 Ok(false)
1586 })
1587 }
1588
1589 pub fn minimum_value(&self) -> Result<f64> {
1590 self.resolve(|node| Ok(node.min_numeric_value().unwrap_or(f64::MIN)))
1591 }
1592
1593 pub fn maximum_value(&self) -> Result<f64> {
1594 self.resolve(|node| Ok(node.max_numeric_value().unwrap_or(f64::MAX)))
1595 }
1596
1597 pub fn minimum_increment(&self) -> Result<f64> {
1598 self.resolve(|node| Ok(node.numeric_value_step().unwrap_or(0.0)))
1599 }
1600
1601 pub fn current_value(&self) -> Result<f64> {
1602 self.resolve(|node| {
1603 let wrapper = NodeWrapper(&node);
1604 Ok(wrapper.current_value().unwrap_or(0.0))
1605 })
1606 }
1607
1608 pub fn set_current_value(&self, value: f64) -> Result<()> {
1609 self.do_action_internal(self.id, |_, _, target_node, target_tree| ActionRequest {
1610 action: Action::SetValue,
1611 target_tree,
1612 target_node,
1613 data: Some(ActionData::NumericValue(value)),
1614 })
1615 }
1616}
1617
1618impl PartialEq for PlatformNode {
1619 fn eq(&self, other: &Self) -> bool {
1620 self.adapter_id == other.adapter_id && self.id == other.id
1621 }
1622}
1623
1624impl Eq for PlatformNode {}
1625
1626impl Hash for PlatformNode {
1627 fn hash<H: Hasher>(&self, state: &mut H) {
1628 self.adapter_id.hash(state);
1629 self.id.hash(state);
1630 }
1631}
1632
1633#[derive(Clone)]
1634pub struct PlatformRoot {
1635 app_context: Weak<RwLock<AppContext>>,
1636}
1637
1638impl PlatformRoot {
1639 pub fn new(app_context: &Arc<RwLock<AppContext>>) -> Self {
1640 Self {
1641 app_context: Arc::downgrade(app_context),
1642 }
1643 }
1644
1645 fn resolve_app_context<F, T>(&self, f: F) -> Result<T>
1646 where
1647 for<'a> F: FnOnce(RwLockReadGuard<'a, AppContext>) -> Result<T>,
1648 {
1649 let app_context = match self.app_context.upgrade() {
1650 Some(context) => context,
1651 None => return Err(Error::Defunct),
1652 };
1653 let app_context = app_context.read().unwrap();
1654 f(app_context)
1655 }
1656
1657 pub fn name(&self) -> Result<String> {
1658 self.resolve_app_context(|context| Ok(context.name.clone().unwrap_or_default()))
1659 }
1660
1661 pub fn child_count(&self) -> Result<i32> {
1662 self.resolve_app_context(|context| {
1663 i32::try_from(context.adapters.len()).map_err(|_| Error::TooManyChildren)
1664 })
1665 }
1666
1667 pub fn child_at_index(&self, index: usize) -> Result<Option<PlatformNode>> {
1668 self.resolve_app_context(|context| {
1669 let child = context
1670 .adapters
1671 .get(index)
1672 .map(PlatformNode::from_adapter_root);
1673 Ok(child)
1674 })
1675 }
1676
1677 pub fn child_id_at_index(&self, index: usize) -> Result<Option<(usize, NodeId)>> {
1678 self.resolve_app_context(|context| {
1679 let child = context
1680 .adapters
1681 .get(index)
1682 .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()));
1683 Ok(child)
1684 })
1685 }
1686
1687 pub fn map_children<T, I>(&self, f: impl Fn(PlatformNode) -> I) -> Result<T>
1688 where
1689 T: FromIterator<I>,
1690 {
1691 self.resolve_app_context(|context| {
1692 let children = context
1693 .adapters
1694 .iter()
1695 .map(PlatformNode::from_adapter_root)
1696 .map(f)
1697 .collect();
1698 Ok(children)
1699 })
1700 }
1701
1702 pub fn map_child_ids<T, I>(&self, f: impl Fn((usize, NodeId)) -> I) -> Result<T>
1703 where
1704 T: FromIterator<I>,
1705 {
1706 self.resolve_app_context(|context| {
1707 let children = context
1708 .adapters
1709 .iter()
1710 .map(|(adapter_id, context)| (*adapter_id, context.read_tree().state().root_id()))
1711 .map(f)
1712 .collect();
1713 Ok(children)
1714 })
1715 }
1716
1717 pub fn toolkit_name(&self) -> Result<String> {
1718 self.resolve_app_context(|context| Ok(context.toolkit_name.clone().unwrap_or_default()))
1719 }
1720
1721 pub fn toolkit_version(&self) -> Result<String> {
1722 self.resolve_app_context(|context| Ok(context.toolkit_version.clone().unwrap_or_default()))
1723 }
1724
1725 pub fn id(&self) -> Result<i32> {
1726 self.resolve_app_context(|context| Ok(context.id.unwrap_or(-1)))
1727 }
1728
1729 pub fn set_id(&mut self, id: i32) -> Result<()> {
1730 let app_context = match self.app_context.upgrade() {
1731 Some(context) => context,
1732 None => return Err(Error::Defunct),
1733 };
1734 let mut app_context = app_context.write().unwrap();
1735 app_context.id = Some(id);
1736 Ok(())
1737 }
1738}
1739
1740impl PartialEq for PlatformRoot {
1741 fn eq(&self, other: &Self) -> bool {
1742 self.app_context.ptr_eq(&other.app_context)
1743 }
1744}
1745
1746impl Hash for PlatformRoot {
1747 fn hash<H: Hasher>(&self, state: &mut H) {
1748 self.app_context.as_ptr().hash(state);
1749 }
1750}
1751
1752#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1753pub enum NodeIdOrRoot {
1754 Node(NodeId),
1755 Root,
1756}