From b006d6d9dea11ac788a54ee3ffd13a9463003a32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 5 Jun 2023 18:41:11 +0200 Subject: [PATCH] iOS: Don't invalidate a11y whenever UI elements are added or removed The UIAccessibilityScreenChangedNotification will result in iOS resetting its state, focusing the first a11y element on the screen. We shouldn't tie clearing the a11y cache to this notification, as those are two separate actions. In the case of adding or removing individual elements, we still likely need to clear the cache, but can inform the system of the more granular UIAccessibilityLayoutChangedNotification to have it re-read the a11y tree. We still handle additions and removal of a11y elements with Window or Dialog roles as UIAccessibilityScreenChangedNotification, as these likely involve major UI changes. The implicit UIAccessibilityScreenChangedNotification on QIOSWindow destruction has been removed, as it's assumed iOS will automatically refresh its a11y tree when a UIWindow is destroyed, and in any case it's up to the individual clients of QAccessible to send the relevant QAccessibleEvent to inform about the situation. Pick-to: 6.6 6.5 Fixes: QTBUG-100094 Change-Id: If7d5cb961743e5ca97d45553b05ae5e92f82d275 Reviewed-by: Richard Moe Gustavsen Reviewed-by: Timur Pocheptsov --- .../platforms/ios/qiosplatformaccessibility.mm | 17 +++++++++++++---- src/plugins/platforms/ios/qioswindow.mm | 1 + .../platforms/ios/quiview_accessibility.mm | 1 - 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm index d54b7db57a..f22782fb04 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.mm +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm @@ -25,8 +25,6 @@ void invalidateCache(QAccessibleInterface *iface) // This will invalidate everything regardless of what window the // interface belonged to. We might want to revisit this strategy later. // (Therefore this function still takes the interface as argument) - // It is also responsible for the bug that focus gets temporary lost - // when items get added or removed from the screen foreach (QWindow *win, QGuiApplication::topLevelWindows()) { if (win && win->handle()) { QT_PREPEND_NAMESPACE(QIOSWindow) *window = static_cast(win->handle()); @@ -38,14 +36,25 @@ void invalidateCache(QAccessibleInterface *iface) void QIOSPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { - if (!isActive() || !event->accessibleInterface()) + auto *accessibleInterface = event->accessibleInterface(); + if (!isActive() || !accessibleInterface) return; switch (event->type()) { case QAccessible::ObjectCreated: case QAccessible::ObjectShow: case QAccessible::ObjectHide: case QAccessible::ObjectDestroyed: - invalidateCache(event->accessibleInterface()); + invalidateCache(accessibleInterface); + switch (accessibleInterface->role()) { + case QAccessible::Window: + case QAccessible::Dialog: + // Bigger changes to the UI require a full reset of VoiceOver + UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil); + break; + default: + // While smaller changes can be handled by re-reading the layout + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + } break; default: break; diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 99f9e38846..8de094533b 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -75,6 +75,7 @@ QIOSWindow::~QIOSWindow() [m_view touchesCancelled:[NSSet set] withEvent:0]; clearAccessibleCache(); + m_view.platformWindow = 0; [m_view removeFromSuperview]; [m_view release]; diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm index 366141ef81..04e1f8cfb3 100644 --- a/src/plugins/platforms/ios/quiview_accessibility.mm +++ b/src/plugins/platforms/ios/quiview_accessibility.mm @@ -54,7 +54,6 @@ - (void)clearAccessibleCache { [m_accessibleElements removeAllObjects]; - UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @""); } // this is a container, returning yes here means the functions below will never be called