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