7#if QT_CONFIG(accessibility)
9#include "private/qaccessiblecache_p.h"
10#include "private/qcore_mac_p.h"
11#include "uistrings_p.h"
12#include "qioswindow.h"
14#include <QtGui/private/qaccessiblebridgeutils_p.h>
16QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
18@implementation QMacAccessibilityElement
20- (instancetype)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view
22 Q_ASSERT((
int)anId < 0);
23 self = [super initWithAccessibilityContainer:view];
30+ (instancetype)elementWithId:(QAccessible::Id)anId
36 QAccessibleCache *cache = QAccessibleCache::instance();
38 QMacAccessibilityElement *element = cache->elementForId(anId);
40 QWindow *window =
nullptr;
41 auto *iface = QAccessible::accessibleInterface(anId);
43 if ((window = iface->window()))
45 iface = iface->parent();
48 if (window && window->handle()) {
49 auto *platformWindow =
static_cast<QIOSWindow*>(window->handle());
50 element = [[self alloc] initWithId:anId withAccessibilityContainer:platformWindow->view()];
51 if (cache->insertElement(anId, element))
54 qWarning() <<
"Could not create a11y element for" << iface
55 <<
"with window" << window
56 <<
"and platform window" << (window ? window->handle() :
nullptr);
66- (BOOL)isAccessibilityElement
71- (NSString*)accessibilityLabel
73 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
74 if (!iface || !iface->isValid()) {
75 qWarning() <<
"invalid accessible interface for: " << self.axid;
79 return iface->text(QAccessible::Name).toNSString();
83- (NSString*)accessibilityIdentifier
85 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
86 if (!iface || !iface->isValid()) {
87 qWarning() <<
"invalid accessible interface for: " << self.axid;
90 return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
93- (NSString*)accessibilityHint
95 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
96 if (!iface || !iface->isValid()) {
97 qWarning() <<
"invalid accessible interface for: " << self.axid;
100 return iface->text(QAccessible::Description).toNSString();
103- (NSString*)accessibilityValue
105 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
106 if (!iface || !iface->isValid()) {
107 qWarning() <<
"invalid accessible interface for: " << self.axid;
111 QAccessible::State state = iface->state();
113 if (state.checkable) {
114 if (iface->role() == QAccessible::CheckBox
115 || iface->role() == QAccessible::RadioButton
116 || iface->role() == QAccessible::Switch)
117 return state.checked ? @
"1" : @
"0";
120 ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString()
121 : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString();
124 QAccessibleValueInterface *val = iface->valueInterface();
126 return val->currentValue().toString().toNSString();
127 }
else if (iface->editableTextInterface()) {
128 if (QAccessibleTextInterface *text = iface->textInterface())
129 return text->text(0, text->characterCount()).toNSString();
132 return [super accessibilityValue];
135- (CGRect)accessibilityFrame
137 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
138 if (!iface || !iface->isValid()) {
139 qWarning() <<
"invalid accessible interface for: " << self.axid;
143 QRect rect = iface->rect();
144 CGRect frame = QRectF(rect).toCGRect();
148 UIView *view = self.accessibilityContainer;
149 if ([view isKindOfClass:[UIView
class]]) {
150 if (UIView *rootView = view.window.rootViewController.view) {
151 CATransform3D transform = rootView.layer.sublayerTransform;
152 if (!CATransform3DIsIdentity(transform))
153 frame.origin.y += transform.m42;
160- (UIAccessibilityTraits)accessibilityTraits
162 UIAccessibilityTraits traits = UIAccessibilityTraitNone;
164 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
165 if (!iface || !iface->isValid()) {
166 qWarning() <<
"invalid accessible interface for: " << self.axid;
169 QAccessible::State state = iface->state();
171 traits |= UIAccessibilityTraitNotEnabled;
173 if (state.searchEdit)
174 traits |= UIAccessibilityTraitSearchField;
177 traits |= UIAccessibilityTraitSelected;
179 const auto accessibleRole = iface->role();
180 if (accessibleRole == QAccessible::Button) {
181 traits |= UIAccessibilityTraitButton;
182 }
else if (accessibleRole == QAccessible::CheckBox
183 || accessibleRole == QAccessible::RadioButton
184 || accessibleRole == QAccessible::Switch) {
185 traits |= UIAccessibilityTraitToggleButton;
186 }
else if (accessibleRole == QAccessible::EditableText) {
187 static auto defaultTextFieldTraits = []{
188 auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease];
189 return textField.accessibilityTraits;
191 traits |= defaultTextFieldTraits;
192 }
else if (accessibleRole == QAccessible::Graphic) {
193 traits |= UIAccessibilityTraitImage;
194 }
else if (accessibleRole == QAccessible::Heading) {
195 traits |= UIAccessibilityTraitHeader;
196 }
else if (accessibleRole == QAccessible::Link) {
197 traits |= UIAccessibilityTraitLink;
198 }
else if (accessibleRole == QAccessible::StaticText) {
199 traits |= UIAccessibilityTraitStaticText;
202 if (iface->valueInterface() && iface->role() != QAccessible::ProgressBar)
203 traits |= UIAccessibilityTraitAdjustable;
208- (BOOL)accessibilityActivate
210 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
211 if (!iface || !iface->isValid()) {
212 qWarning() <<
"invalid accessible interface for: " << self.axid;
216 if (QAccessibleActionInterface *action = iface->actionInterface()) {
217 if (action->actionNames().contains(QAccessibleActionInterface::pressAction())) {
218 action->doAction(QAccessibleActionInterface::pressAction());
220 }
else if (action->actionNames().contains(QAccessibleActionInterface::showMenuAction())) {
221 action->doAction(QAccessibleActionInterface::showMenuAction());
228- (
void)accessibilityIncrement
230 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
231 if (!iface || !iface->isValid()) {
232 qWarning() <<
"invalid accessible interface for: " << self.axid;
236 if (QAccessibleActionInterface *action = iface->actionInterface())
237 action->doAction(QAccessibleActionInterface::increaseAction());
240- (
void)accessibilityDecrement
242 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
243 if (!iface || !iface->isValid()) {
244 qWarning() <<
"invalid accessible interface for: " << self.axid;
248 if (QAccessibleActionInterface *action = iface->actionInterface())
249 action->doAction(QAccessibleActionInterface::decreaseAction());
252- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
254 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
255 if (!iface || !iface->isValid()) {
256 qWarning() <<
"invalid accessible interface for: " << self.axid;
260 QAccessibleActionInterface *action = iface->actionInterface();
264 case UIAccessibilityScrollDirectionRight:
265 action->doAction(QAccessibleActionInterface::scrollRightAction());
267 case UIAccessibilityScrollDirectionLeft:
268 action->doAction(QAccessibleActionInterface::scrollLeftAction());
270 case UIAccessibilityScrollDirectionUp:
271 action->doAction(QAccessibleActionInterface::scrollUpAction());
273 case UIAccessibilityScrollDirectionDown:
274 action->doAction(QAccessibleActionInterface::scrollDownAction());
276 case UIAccessibilityScrollDirectionNext:
277 action->doAction(QAccessibleActionInterface::nextPageAction());
279 case UIAccessibilityScrollDirectionPrevious:
280 action->doAction(QAccessibleActionInterface::previousPageAction());
286- (NSString *)accessibilityLanguage
288 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
289 if (!iface || !iface->isValid()) {
290 qWarning() <<
"invalid accessible interface for: " << self.axid;
293 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
294 if (!attributesIface || !attributesIface->attributeKeys().contains(QAccessible::Attribute::Locale))
297 const auto &localeVariant = attributesIface->attributeValue(QAccessible::Attribute::Locale);
298 return localeVariant.toLocale().bcp47Name().toNSString();