diff --git a/consumer/src/node.rs b/consumer/src/node.rs index c5b3f54f..402e266d 100644 --- a/consumer/src/node.rs +++ b/consumer/src/node.rs @@ -354,6 +354,14 @@ impl NodeState { self.data().role() } + pub fn role_description(&self) -> Option { + self.data().role_description().map(String::from) + } + + pub fn has_role_description(&self) -> bool { + self.data().role_description().is_some() + } + pub fn is_hidden(&self) -> bool { self.data().is_hidden() } diff --git a/platforms/macos/src/node.rs b/platforms/macos/src/node.rs index 35460196..42ef43fe 100644 --- a/platforms/macos/src/node.rs +++ b/platforms/macos/src/node.rs @@ -18,7 +18,7 @@ use objc2::{ foundation::{ NSArray, NSCopying, NSInteger, NSNumber, NSObject, NSPoint, NSRange, NSRect, NSString, }, - msg_send_id, ns_string, + msg_send, msg_send_id, ns_string, rc::{Id, Owned, Shared}, runtime::Sel, sel, ClassType, @@ -400,6 +400,18 @@ declare_class!( Id::autorelease_return(role.copy()) } + #[sel(accessibilityRoleDescription)] + fn role_description(&self) -> *mut NSString { + self.resolve(|node| { + if let Some(role_description) = node.role_description() { + Id::autorelease_return(NSString::from_str(&role_description)) + } else { + unsafe { msg_send![super(self), accessibilityRoleDescription] } + } + }) + .unwrap_or_else(null_mut) + } + #[sel(accessibilityTitle)] fn title(&self) -> *mut NSString { let result = self diff --git a/platforms/unix/src/atspi/interfaces/accessible.rs b/platforms/unix/src/atspi/interfaces/accessible.rs index e5fed9e3..d1113e60 100644 --- a/platforms/unix/src/atspi/interfaces/accessible.rs +++ b/platforms/unix/src/atspi/interfaces/accessible.rs @@ -83,6 +83,10 @@ impl AccessibleInterface { self.node.role() } + fn get_localized_role_name(&self) -> fdo::Result { + self.node.localized_role_name() + } + fn get_state(&self) -> fdo::Result { self.node.state() } diff --git a/platforms/unix/src/node.rs b/platforms/unix/src/node.rs index 94567fc3..2fc1f919 100644 --- a/platforms/unix/src/node.rs +++ b/platforms/unix/src/node.rs @@ -110,6 +110,10 @@ impl<'a> NodeWrapper<'a> { } pub fn role(&self) -> AtspiRole { + if self.node_state().has_role_description() { + return AtspiRole::Extended; + } + match self.node_state().role() { Role::Alert => AtspiRole::Notification, Role::AlertDialog => AtspiRole::Alert, @@ -813,6 +817,10 @@ impl PlatformNode { }) } + pub(crate) fn localized_role_name(&self) -> fdo::Result { + self.resolve(|node| Ok(node.state().role_description().unwrap_or_default())) + } + pub fn state(&self) -> fdo::Result { self.resolve_with_context(|node, context| { let wrapper = self.node_wrapper(&node); diff --git a/platforms/windows/src/node.rs b/platforms/windows/src/node.rs index b1c3cd2e..fe4649d3 100644 --- a/platforms/windows/src/node.rs +++ b/platforms/windows/src/node.rs @@ -266,6 +266,10 @@ impl<'a> NodeWrapper<'a> { } } + fn localized_control_type(&self) -> Option { + self.node_state().role_description() + } + fn name(&self) -> Option { match self { Self::Node(node) => node.name(), @@ -841,6 +845,7 @@ macro_rules! patterns { properties! { (ControlType, control_type), + (LocalizedControlType, localized_control_type), (Name, name), (IsContentElement, is_content_element), (IsControlElement, is_content_element),