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
qiosplatformaccessibility.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
5#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
6
8
9#if QT_CONFIG(accessibility)
10
11#include <QtGui/QtGui>
12#include "qioswindow.h"
13#include "quiaccessibilityelement.h"
14
15QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
16
17QIOSPlatformAccessibility::QIOSPlatformAccessibility()
18{
19 m_focusObserver = QMacNotificationObserver(
20 nil, UIAccessibilityElementFocusedNotification, [&](NSNotification *notification) {
21 id element = notification.userInfo[UIAccessibilityFocusedElementKey];
22 m_focusElement = static_cast<QMacAccessibilityElement *>(element);
23 });
24}
25
26QIOSPlatformAccessibility::~QIOSPlatformAccessibility()
27{}
28
29
30void invalidateCache(QAccessibleInterface *iface)
31{
32 if (!iface || !iface->isValid()) {
33 qWarning() << "invalid accessible interface: " << iface;
34 return;
35 }
36
37 // This will invalidate everything regardless of what window the
38 // interface belonged to. We might want to revisit this strategy later.
39 // (Therefore this function still takes the interface as argument)
40 foreach (QWindow *win, QGuiApplication::topLevelWindows()) {
41 if (win && win->handle()) {
42 QT_PREPEND_NAMESPACE(QIOSWindow) *window = static_cast<QT_PREPEND_NAMESPACE(QIOSWindow) *>(win->handle());
43 window->clearAccessibleCache();
44 }
45 }
46}
47
48
49void QIOSPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
50{
51 auto *accessibleInterface = event->accessibleInterface();
52 if (!isActive() || !accessibleInterface)
53 return;
54 switch (event->type()) {
55 case QAccessible::Announcement: {
56 auto *announcementEvent = static_cast<QAccessibleAnnouncementEvent *>(event);
57 const bool queueAnnouncement =
58 (announcementEvent->politeness() == QAccessible::AnnouncementPoliteness::Polite);
59 NSAttributedString *message = [[NSAttributedString alloc]
60 initWithString:announcementEvent->message().toNSString()
61 attributes:@{UIAccessibilitySpeechAttributeQueueAnnouncement: @(queueAnnouncement)}];
62 UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, [message autorelease]);
63 break;
64 }
65 case QAccessible::Focus: {
66 auto *element = [QMacAccessibilityElement elementWithId:event->uniqueId()];
67 Q_ASSERT(element);
68 // There's no NSAccessibilityFocusedUIElementChangedNotification, like we have on
69 // macOS. Instead, the documentation for UIAccessibilityLayoutChangedNotification
70 // specifies that the optional argument to UIAccessibilityPostNotification is the
71 // accessibility element for VoiceOver to move to after processing the notification.
72 UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element);
73 break;
74 }
75 case QAccessible::DescriptionChanged:
76 case QAccessible::NameChanged: {
77 auto *element = [QMacAccessibilityElement elementWithId:event->uniqueId()];
78 if (element == m_focusElement)
79 UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element);
80 break;
81 }
82 case QAccessible::ObjectCreated:
83 case QAccessible::ObjectShow:
84 case QAccessible::ObjectHide:
85 case QAccessible::ObjectDestroyed:
86 invalidateCache(accessibleInterface);
87 switch (accessibleInterface->role()) {
88 case QAccessible::Window:
89 case QAccessible::Dialog:
90 // Bigger changes to the UI require a full reset of VoiceOver
91 UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
92 break;
93 default:
94 // While smaller changes can be handled by re-reading the layout
95 UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
96 }
97 break;
98 default:
99 break;
100 }
101}
102
103#endif