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 accessibilityHint];
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 return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
147- (UIAccessibilityTraits)accessibilityTraits
149 UIAccessibilityTraits traits = UIAccessibilityTraitNone;
151 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
152 if (!iface || !iface->isValid()) {
153 qWarning() <<
"invalid accessible interface for: " << self.axid;
156 QAccessible::State state = iface->state();
158 traits |= UIAccessibilityTraitNotEnabled;
160 if (state.searchEdit)
161 traits |= UIAccessibilityTraitSearchField;
164 traits |= UIAccessibilityTraitSelected;
166 const auto accessibleRole = iface->role();
167 if (accessibleRole == QAccessible::Button) {
168 traits |= UIAccessibilityTraitButton;
169 }
else if (accessibleRole == QAccessible::CheckBox
170 || accessibleRole == QAccessible::RadioButton
171 || accessibleRole == QAccessible::Switch) {
172 traits |= UIAccessibilityTraitToggleButton;
173 }
else if (accessibleRole == QAccessible::EditableText) {
174 static auto defaultTextFieldTraits = []{
175 auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease];
176 return textField.accessibilityTraits;
178 traits |= defaultTextFieldTraits;
179 }
else if (accessibleRole == QAccessible::Graphic) {
180 traits |= UIAccessibilityTraitImage;
181 }
else if (accessibleRole == QAccessible::Heading) {
182 traits |= UIAccessibilityTraitHeader;
183 }
else if (accessibleRole == QAccessible::Link) {
184 traits |= UIAccessibilityTraitLink;
185 }
else if (accessibleRole == QAccessible::StaticText) {
186 traits |= UIAccessibilityTraitStaticText;
189 if (iface->valueInterface() && iface->role() != QAccessible::ProgressBar)
190 traits |= UIAccessibilityTraitAdjustable;
195- (BOOL)accessibilityActivate
197 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
198 if (!iface || !iface->isValid()) {
199 qWarning() <<
"invalid accessible interface for: " << self.axid;
203 if (QAccessibleActionInterface *action = iface->actionInterface()) {
204 if (action->actionNames().contains(QAccessibleActionInterface::pressAction())) {
205 action->doAction(QAccessibleActionInterface::pressAction());
207 }
else if (action->actionNames().contains(QAccessibleActionInterface::showMenuAction())) {
208 action->doAction(QAccessibleActionInterface::showMenuAction());
215- (
void)accessibilityIncrement
217 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
218 if (!iface || !iface->isValid()) {
219 qWarning() <<
"invalid accessible interface for: " << self.axid;
223 if (QAccessibleActionInterface *action = iface->actionInterface())
224 action->doAction(QAccessibleActionInterface::increaseAction());
227- (
void)accessibilityDecrement
229 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
230 if (!iface || !iface->isValid()) {
231 qWarning() <<
"invalid accessible interface for: " << self.axid;
235 if (QAccessibleActionInterface *action = iface->actionInterface())
236 action->doAction(QAccessibleActionInterface::decreaseAction());
239- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
241 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
242 if (!iface || !iface->isValid()) {
243 qWarning() <<
"invalid accessible interface for: " << self.axid;
247 QAccessibleActionInterface *action = iface->actionInterface();
251 case UIAccessibilityScrollDirectionRight:
252 action->doAction(QAccessibleActionInterface::scrollRightAction());
254 case UIAccessibilityScrollDirectionLeft:
255 action->doAction(QAccessibleActionInterface::scrollLeftAction());
257 case UIAccessibilityScrollDirectionUp:
258 action->doAction(QAccessibleActionInterface::scrollUpAction());
260 case UIAccessibilityScrollDirectionDown:
261 action->doAction(QAccessibleActionInterface::scrollDownAction());
263 case UIAccessibilityScrollDirectionNext:
264 action->doAction(QAccessibleActionInterface::nextPageAction());
266 case UIAccessibilityScrollDirectionPrevious:
267 action->doAction(QAccessibleActionInterface::previousPageAction());
273- (NSString *)accessibilityLanguage
275 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
276 if (!iface || !iface->isValid()) {
277 qWarning() <<
"invalid accessible interface for: " << self.axid;
280 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
281 if (!attributesIface || !attributesIface->attributeKeys().contains(QAccessible::Attribute::Locale))
284 const auto &localeVariant = attributesIface->attributeValue(QAccessible::Attribute::Locale);
285 return localeVariant.toLocale().bcp47Name().toNSString();