From 1c8cb94e4ab7b6255c95caebf7d7f545a1e9a0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 5 Feb 2024 17:14:34 +0100 Subject: [PATCH] iOS: Propagate focus changes to VoiceOver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changing the focus object should update the VoiceOver focused element, the same way we do it on macOS. There's no NSAccessibilityFocusedUIElementChangedNotification on iOS, but we can pass the focused element as an argument when posting the UIAccessibilityLayoutChangedNotification. The class method on QMacAccessibilityElement to get an element for an QAccessible::Id was not used by any callers, and has been modified to resolve the container from the QAccessibleInterface, so that we don't need to plumb that all the way from the focus change event. Inspired-by: Jan Möller Fixes: QTBUG-114608 Pick-to: 6.6 6.5 Change-Id: I2e43ae649bc7e3a44c1e1200e8de66bf420b1949 Reviewed-by: Lars Schmertmann Reviewed-by: Volker Hilsheimer (cherry picked from commit 7a512d1267442e646bb7942291197b2b03f4d1cd) Reviewed-by: Qt Cherry-pick Bot --- .../platforms/ios/qiosplatformaccessibility.mm | 11 +++++++++++ src/plugins/platforms/ios/qioswindow.h | 1 + src/plugins/platforms/ios/qioswindow.mm | 5 +++++ .../platforms/ios/quiaccessibilityelement.h | 2 +- .../platforms/ios/quiaccessibilityelement.mm | 17 +++++++++++++---- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm index f128ed6d67a..eb18ee637ee 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.mm +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm @@ -9,6 +9,7 @@ #include #include "qioswindow.h" +#include "quiaccessibilityelement.h" QIOSPlatformAccessibility::QIOSPlatformAccessibility() {} @@ -42,6 +43,16 @@ void QIOSPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *even if (!isActive() || !accessibleInterface) return; switch (event->type()) { + case QAccessible::Focus: { + auto *element = [QMacAccessibilityElement elementWithId:event->uniqueId()]; + Q_ASSERT(element); + // There's no NSAccessibilityFocusedUIElementChangedNotification, like we have on + // macOS. Instead, the documentation for UIAccessibilityLayoutChangedNotification + // specifies that the optional argument to UIAccessibilityPostNotification is the + // accessibility element for VoiceOver to move to after processing the notification. + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element); + break; + } case QAccessible::ObjectCreated: case QAccessible::ObjectShow: case QAccessible::ObjectHide: diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 1634213e06e..02b21612023 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -63,6 +63,7 @@ public: #endif bool isForeignWindow() const override; + UIView *view() const; private: void applicationStateChanged(Qt::ApplicationState state); diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 347c7b9df13..a0ba6213b91 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -458,6 +458,11 @@ bool QIOSWindow::isForeignWindow() const return ![m_view isKindOfClass:QUIView.class]; } +UIView *QIOSWindow::view() const +{ + return m_view; +} + QT_END_NAMESPACE #include "moc_qioswindow.cpp" diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.h b/src/plugins/platforms/ios/quiaccessibilityelement.h index e78fef6d309..85803254364 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.h +++ b/src/plugins/platforms/ios/quiaccessibilityelement.h @@ -14,7 +14,7 @@ @property (readonly) QAccessible::Id axid; - (instancetype)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; -+ (instancetype)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; ++ (instancetype)elementWithId:(QAccessible::Id)anId; @end diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index 08e366f32bf..39b2cb8a50f 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -8,6 +8,7 @@ #include "private/qaccessiblecache_p.h" #include "private/qcore_mac_p.h" #include "uistrings_p.h" +#include "qioswindow.h" QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); @@ -23,7 +24,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); return self; } -+ (instancetype)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view ++ (instancetype)elementWithId:(QAccessible::Id)anId { Q_ASSERT(anId); if (!anId) @@ -33,9 +34,17 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); QMacAccessibilityElement *element = cache->elementForId(anId); if (!element) { - Q_ASSERT(QAccessible::accessibleInterface(anId)); - element = [[self alloc] initWithId:anId withAccessibilityContainer:view]; - cache->insertElement(anId, element); + auto *a11yInterface = QAccessible::accessibleInterface(anId); + Q_ASSERT(a11yInterface); + auto *window = a11yInterface->window(); + if (window && window->handle()) { + auto *platformWindow = static_cast(window->handle()); + element = [[self alloc] initWithId:anId withAccessibilityContainer:platformWindow->view()]; + cache->insertElement(anId, element); + } else { + qWarning() << "Could not create a11y element for" << window + << "with platform window" << (window ? window->handle() : nullptr); + } } return element; }