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 accessibilityValue];
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 CGRect frame = QRectF(rect).toCGRect();
145
146 // Adjust the frame to ensure focus rectangle aligns with the visually shifted
147 // content when onscreen keyboard is shown.
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;
154 }
155 }
156
157 return frame;
158}
159
160- (UIAccessibilityTraits)accessibilityTraits
161{
162 UIAccessibilityTraits traits = UIAccessibilityTraitNone;
163
164 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
165 if (!iface || !iface->isValid()) {
166 qWarning() << "invalid accessible interface for: " << self.axid;
167 return traits;
168 }
169 QAccessible::State state = iface->state();
170 if (state.disabled)
171 traits |= UIAccessibilityTraitNotEnabled;
172
173 if (state.searchEdit)
174 traits |= UIAccessibilityTraitSearchField;
175
176 if (state.selected)
177 traits |= UIAccessibilityTraitSelected;
178
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;
190 }();
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;
200 }
201
202 if (iface->valueInterface() && iface->role() != QAccessible::ProgressBar)
203 traits |= UIAccessibilityTraitAdjustable;
204
205 return traits;
206}
207
208- (BOOL)accessibilityActivate
209{
210 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
211 if (!iface || !iface->isValid()) {
212 qWarning() << "invalid accessible interface for: " << self.axid;
213 return NO;
214 }
215
216 if (QAccessibleActionInterface *action = iface->actionInterface()) {
217 if (action->actionNames().contains(QAccessibleActionInterface::pressAction())) {
218 action->doAction(QAccessibleActionInterface::pressAction());
219 return YES;
220 } else if (action->actionNames().contains(QAccessibleActionInterface::showMenuAction())) {
221 action->doAction(QAccessibleActionInterface::showMenuAction());
222 return YES;
223 }
224 }
225 return NO; // fall back to sending mouse clicks
226}
227
228- (void)accessibilityIncrement
229{
230 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
231 if (!iface || !iface->isValid()) {
232 qWarning() << "invalid accessible interface for: " << self.axid;
233 return;
234 }
235
236 if (QAccessibleActionInterface *action = iface->actionInterface())
237 action->doAction(QAccessibleActionInterface::increaseAction());
238}
239
240- (void)accessibilityDecrement
241{
242 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
243 if (!iface || !iface->isValid()) {
244 qWarning() << "invalid accessible interface for: " << self.axid;
245 return;
246 }
247
248 if (QAccessibleActionInterface *action = iface->actionInterface())
249 action->doAction(QAccessibleActionInterface::decreaseAction());
250}
251
252- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
253{
254 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
255 if (!iface || !iface->isValid()) {
256 qWarning() << "invalid accessible interface for: " << self.axid;
257 return NO;
258 }
259
260 QAccessibleActionInterface *action = iface->actionInterface();
261 if (!action)
262 return NO;
263 switch (direction) {
264 case UIAccessibilityScrollDirectionRight:
265 action->doAction(QAccessibleActionInterface::scrollRightAction());
266 return YES;
267 case UIAccessibilityScrollDirectionLeft:
268 action->doAction(QAccessibleActionInterface::scrollLeftAction());
269 return YES;
270 case UIAccessibilityScrollDirectionUp:
271 action->doAction(QAccessibleActionInterface::scrollUpAction());
272 return YES;
273 case UIAccessibilityScrollDirectionDown:
274 action->doAction(QAccessibleActionInterface::scrollDownAction());
275 return YES;
276 case UIAccessibilityScrollDirectionNext:
277 action->doAction(QAccessibleActionInterface::nextPageAction());
278 return YES;
279 case UIAccessibilityScrollDirectionPrevious:
280 action->doAction(QAccessibleActionInterface::previousPageAction());
281 return YES;
282 }
283 return NO;
284}
285
286- (NSString *)accessibilityLanguage
287{
288 QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
289 if (!iface || !iface->isValid()) {
290 qWarning() << "invalid accessible interface for: " << self.axid;
291 return @"";
292 }
293 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
294 if (!attributesIface || !attributesIface->attributeKeys().contains(QAccessible::Attribute::Locale))
295 return @"";
296
297 const auto &localeVariant = attributesIface->attributeValue(QAccessible::Attribute::Locale);
298 return localeVariant.toLocale().bcp47Name().toNSString();
299}
300
301@end
302
303#endif