Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
quiaccessibilityelement.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#if QT_CONFIG(accessibility)
8
9#include "private/qaccessiblecache_p.h"
10#include "private/qcore_mac_p.h"
11#include "uistrings_p.h"
12#include "qioswindow.h"
13
14#include <QtGui/private/qaccessiblebridgeutils_p.h>
15
16QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
17
18@implementation QMacAccessibilityElement
19
20- (instancetype)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view
21{
22 Q_ASSERT((int)anId < 0);
23 self = [super initWithAccessibilityContainer:view];
24 if (self)
25 _axid = anId;
26
27 return self;
28}
29
30+ (instancetype)elementWithId:(QAccessible::Id)anId
31{
32 Q_ASSERT(anId);
33 if (!anId)
34 return nil;
35
36 QAccessibleCache *cache = QAccessibleCache::instance();
37
38 QMacAccessibilityElement *element = cache->elementForId(anId);
39 if (!element) {
40 QWindow *window = nullptr;
41 auto *iface = QAccessible::accessibleInterface(anId);
42 while (iface) {
43 if ((window = iface->window()))
44 break;
45 iface = iface->parent();
46 }
47
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))
52 [element release];
53 } else {
54 qWarning() << "Could not create a11y element for" << iface
55 << "with window" << window
56 << "and platform window" << (window ? window->handle() : nullptr);
57 }
58 }
59 return element;
60}
61
62- (void)invalidate
63{
64}
65
66- (BOOL)isAccessibilityElement
67{
68 return YES;
69}
70
71- (NSString*)accessibilityLabel
72{
73 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
74 if (!iface || !iface->isValid()) {
75 qWarning() << "invalid accessible interface for: " << self.axid;
76 return @"";
77 }
78
79 return iface->text(QAccessible::Name).toNSString();
80}
81
82
83- (NSString*)accessibilityIdentifier
84{
85 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
86 if (!iface || !iface->isValid()) {
87 qWarning() << "invalid accessible interface for: " << self.axid;
88 return @"";
89 }
90 return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
91}
92
93- (NSString*)accessibilityHint
94{
95 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
96 if (!iface || !iface->isValid()) {
97 qWarning() << "invalid accessible interface for: " << self.axid;
98 return @"";
99 }
100 return iface->text(QAccessible::Description).toNSString();
101}
102
103- (NSString*)accessibilityValue
104{
105 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
106 if (!iface || !iface->isValid()) {
107 qWarning() << "invalid accessible interface for: " << self.axid;
108 return @"";
109 }
110
111 QAccessible::State state = iface->state();
112
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";
118
119 return state.checked
120 ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString()
121 : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString();
122 }
123
124 QAccessibleValueInterface *val = iface->valueInterface();
125 if (val) {
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();
130 }
131
132 return [super accessibilityHint];
133}
134
135- (CGRect)accessibilityFrame
136{
137 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
138 if (!iface || !iface->isValid()) {
139 qWarning() << "invalid accessible interface for: " << self.axid;
140 return CGRect();
141 }
142
143 QRect rect = iface->rect();
144 return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
145}
146
147- (UIAccessibilityTraits)accessibilityTraits
148{
149 UIAccessibilityTraits traits = UIAccessibilityTraitNone;
150
151 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
152 if (!iface || !iface->isValid()) {
153 qWarning() << "invalid accessible interface for: " << self.axid;
154 return traits;
155 }
156 QAccessible::State state = iface->state();
157 if (state.disabled)
158 traits |= UIAccessibilityTraitNotEnabled;
159
160 if (state.searchEdit)
161 traits |= UIAccessibilityTraitSearchField;
162
163 if (state.selected)
164 traits |= UIAccessibilityTraitSelected;
165
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;
177 }();
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;
187 }
188
189 if (iface->valueInterface() && iface->role() != QAccessible::ProgressBar)
190 traits |= UIAccessibilityTraitAdjustable;
191
192 return traits;
193}
194
195- (BOOL)accessibilityActivate
196{
197 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
198 if (!iface || !iface->isValid()) {
199 qWarning() << "invalid accessible interface for: " << self.axid;
200 return NO;
201 }
202
203 if (QAccessibleActionInterface *action = iface->actionInterface()) {
204 if (action->actionNames().contains(QAccessibleActionInterface::pressAction())) {
205 action->doAction(QAccessibleActionInterface::pressAction());
206 return YES;
207 } else if (action->actionNames().contains(QAccessibleActionInterface::showMenuAction())) {
208 action->doAction(QAccessibleActionInterface::showMenuAction());
209 return YES;
210 }
211 }
212 return NO; // fall back to sending mouse clicks
213}
214
215- (void)accessibilityIncrement
216{
217 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
218 if (!iface || !iface->isValid()) {
219 qWarning() << "invalid accessible interface for: " << self.axid;
220 return;
221 }
222
223 if (QAccessibleActionInterface *action = iface->actionInterface())
224 action->doAction(QAccessibleActionInterface::increaseAction());
225}
226
227- (void)accessibilityDecrement
228{
229 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
230 if (!iface || !iface->isValid()) {
231 qWarning() << "invalid accessible interface for: " << self.axid;
232 return;
233 }
234
235 if (QAccessibleActionInterface *action = iface->actionInterface())
236 action->doAction(QAccessibleActionInterface::decreaseAction());
237}
238
239- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
240{
241 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
242 if (!iface || !iface->isValid()) {
243 qWarning() << "invalid accessible interface for: " << self.axid;
244 return NO;
245 }
246
247 QAccessibleActionInterface *action = iface->actionInterface();
248 if (!action)
249 return NO;
250 switch (direction) {
251 case UIAccessibilityScrollDirectionRight:
252 action->doAction(QAccessibleActionInterface::scrollRightAction());
253 return YES;
254 case UIAccessibilityScrollDirectionLeft:
255 action->doAction(QAccessibleActionInterface::scrollLeftAction());
256 return YES;
257 case UIAccessibilityScrollDirectionUp:
258 action->doAction(QAccessibleActionInterface::scrollUpAction());
259 return YES;
260 case UIAccessibilityScrollDirectionDown:
261 action->doAction(QAccessibleActionInterface::scrollDownAction());
262 return YES;
263 case UIAccessibilityScrollDirectionNext:
264 action->doAction(QAccessibleActionInterface::nextPageAction());
265 return YES;
266 case UIAccessibilityScrollDirectionPrevious:
267 action->doAction(QAccessibleActionInterface::previousPageAction());
268 return YES;
269 }
270 return NO;
271}
272
273- (NSString *)accessibilityLanguage
274{
275 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
276 if (!iface || !iface->isValid()) {
277 qWarning() << "invalid accessible interface for: " << self.axid;
278 return @"";
279 }
280 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
281 if (!attributesIface || !attributesIface->attributeKeys().contains(QAccessible::Attribute::Locale))
282 return @"";
283
284 const auto &localeVariant = attributesIface->attributeValue(QAccessible::Attribute::Locale);
285 return localeVariant.toLocale().bcp47Name().toNSString();
286}
287
288@end
289
290#endif