From 010952a55ee9ca9731f8ad15f2773d0bae645840 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Tue, 9 Apr 2024 16:43:21 +0200 Subject: [PATCH] a11y: Add new QAccessibleAnnouncementEvent Add a new QAccessibleAnnouncementEvent that can be used to request the announcement of a message by assistive technologies (most prominently screen readers). The priority/urgency of the message can be specified using one of the values of the newly added AnnouncementPriority enum. As suggested by the maintainer of the Orca screen reader in a similar merge request for GTK [1], add some clarification that assertive priority should only be used for things that are actually worth interrupting the user's current task immediately rather than waiting for the next graceful opportunity like the end of the sentence currently spoken by the screen reader. All of AT-SPI on Linux, UIA on Windows and NSAccessibility on macOS have corresponding API: * Linux/AT-SPI: Announcement event (initially added in commit [2], possibility to set priority/politeness level added in commit [3]) * Windows/UIA: UiaRaiseNotificationEvent [4] * macOS/NSAccessibility: announcementRequested function [5] Bridging to the corresponding platform API in the platform backends is left for separate upcoming commits. [1] https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6461#note_1993454 [2] https://gitlab.gnome.org/GNOME/at-spi2-core/-/commit/26835da29918be2c0da6c08fb70d893de22891d2 [3] https://gitlab.gnome.org/GNOME/at-spi2-core/-/commit/32c9420ec82f07da8033c675a2bbf25e535f0509 [4] https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiaraisenotificationevent [5] https://developer.apple.com/documentation/appkit/nsaccessibility/notification/1530633-announcementrequested [ChangeLog][QtGui][QAccessibleAnnouncementEvent] Added new QAccessibleAnnouncementEvent that can be used to request the announcement of a message by assistive technologies. Task-number: QTBUG-75003 Change-Id: Ief60ae7c5a9c8bf8404ad9b2749e253acc5d77c5 Reviewed-by: Volker Hilsheimer --- src/gui/accessible/qaccessible.cpp | 74 +++++++++++++++++++++++++++ src/gui/accessible/qaccessible.h | 32 ++++++++++++ src/gui/accessible/qaccessible_base.h | 6 +++ 3 files changed, 112 insertions(+) diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index 46bca16dad..b037279e8b 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -173,6 +173,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core"); \value ActionChanged An action has been changed. \value ActiveDescendantChanged \value Alert A system alert (e.g., a message from a QMessageBox) + \value [since 6.8] Announcement The announcement of a message is requested. \value AttributeChanged \value ContextHelpEnd Context help (QWhatsThis) for an object is finished. \value ContextHelpStart Context help (QWhatsThis) for an object is initiated. @@ -449,6 +450,30 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core"); \sa QAccessibleAttributesInterface */ +/*! \enum QAccessible::AnnouncementPriority + This enum describes the priority for announcements used by the + \l QAccessibleAnnouncementEvent. + \since 6.8 + + With \a QAccessible::AnouncementPriority::Polite, assistive technologies + should announce the message at the next graceful opportunity such as at the + end of speaking the current sentence or when the user pauses typing. + + When specifying \a QAccessible::AnouncementPriority::Assertive, assistive + technologies should notify the user immediately. + + Because an interruption might disorient users or cause them to not complete + their current task, \a QAccessible::AnouncementPriority::Assertive should + not be used unless the interruption is imperative. + + \value Polite The announcement has normal priority. + \value Assertive The announcement has high priority and should notify + the user immediately, even if that means interrupting the user's + current task. + + \sa QAccessibleAnnouncementEvent +*/ + /*! \enum QAccessible::InterfaceType @@ -1778,7 +1803,56 @@ QAccessibleTextSelectionEvent::~QAccessibleTextSelectionEvent() { } +/*! + \since 6.8 + \class QAccessibleAnnouncementEvent + \ingroup accessibility + \inmodule QtGui + \brief The QAccessibleAnnouncementEvent is used to request the announcement + of a given message by assistive technologies. + + This class is used with \l QAccessible::updateAccessibility(). +*/ + +/*! \fn QAccessibleAnnouncementEvent::QAccessibleAnnouncementEvent(QObject *object, const QString &message) + + Constructs a new QAccessibleAnnouncementEvent event for \a object + to request the announcement of \a message with priority \l QAccessible::AnnouncementPriority::Polite. + + \l QAccessibleAnnouncementEvent::setPriority can be used to adjust the priority. +*/ + +/*! \fn QAccessibleAnnouncementEvent::QAccessibleAnnouncementEvent(QAccessibleInterface *iface, const QString &message) + + Constructs a new QAccessibleAnnouncementEvent event for \a iface + to request the announcement of \a message with priority \l QAccessible::AnnouncementPriority::Polite. + + \l QAccessibleAnnouncementEvent::setPriority can be used to adjust the priority. +*/ + +/*! \fn QString QAccessibleAnnouncementEvent::message() const + + Returns the message. +*/ + +/*! \fn QAccessible::AnnouncementPriority QAccessibleAnnouncementEvent::priority() const + + Returns the priority. +*/ + +/*! \fn void QAccessibleAnnouncementEvent::setPriority(QAccessible::AnnouncementPriority priority) + + Sets the priority with which the announcement will be requested to \a priority. +*/ + + +/*! + \internal +*/ +QAccessibleAnnouncementEvent::~QAccessibleAnnouncementEvent() +{ +} /*! Returns the QAccessibleInterface associated with the event. diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h index 0a92e76c73..3d8daa4b3c 100644 --- a/src/gui/accessible/qaccessible.h +++ b/src/gui/accessible/qaccessible.h @@ -316,6 +316,7 @@ public: Q_ASSERT(m_type != QAccessible::TextRemoved); Q_ASSERT(m_type != QAccessible::TextUpdated); Q_ASSERT(m_type != QAccessible::TableModelChanged); + Q_ASSERT(m_type != QAccessible::Announcement); } inline QAccessibleEvent(QAccessibleInterface *iface, QAccessible::Event typ) @@ -330,6 +331,7 @@ public: Q_ASSERT(m_type != QAccessible::TextRemoved); Q_ASSERT(m_type != QAccessible::TextUpdated); Q_ASSERT(m_type != QAccessible::TableModelChanged); + Q_ASSERT(m_type != QAccessible::Announcement); m_uniqueId = QAccessible::uniqueId(iface); m_object = iface->object(); } @@ -605,6 +607,36 @@ protected: int m_lastColumn; }; +class Q_GUI_EXPORT QAccessibleAnnouncementEvent : public QAccessibleEvent +{ +public: + inline QAccessibleAnnouncementEvent(QObject *object, const QString &message) + : QAccessibleEvent(object, QAccessible::InvalidEvent) + , m_message(message) + , m_priority(QAccessible::AnnouncementPriority::Polite) + { + m_type = QAccessible::Announcement; + } + + inline QAccessibleAnnouncementEvent(QAccessibleInterface *iface, const QString &message) + : QAccessibleEvent(iface, QAccessible::InvalidEvent) + , m_message(message) + , m_priority(QAccessible::AnnouncementPriority::Polite) + { + m_type = QAccessible::Announcement; + } + + ~QAccessibleAnnouncementEvent(); + + QString message() const { return m_message; } + QAccessible::AnnouncementPriority priority() const { return m_priority; } + void setPriority(QAccessible::AnnouncementPriority priority) { m_priority = priority; }; + +protected: + QString m_message; + QAccessible::AnnouncementPriority m_priority; +}; + #ifndef Q_QDOC #define QAccessibleInterface_iid "org.qt-project.Qt.QAccessibleInterface" Q_DECLARE_INTERFACE(QAccessibleInterface, QAccessibleInterface_iid) diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h index 2d2b1de316..1a7b75c7f5 100644 --- a/src/gui/accessible/qaccessible_base.h +++ b/src/gui/accessible/qaccessible_base.h @@ -101,6 +101,7 @@ public: HelpChanged = 0x80A0, DefaultActionChanged = 0x80B0, AcceleratorChanged = 0x80C0, + Announcement = 0x80D0, InvalidEvent }; @@ -367,6 +368,11 @@ public: Level, }; + enum class AnnouncementPriority { + Polite, + Assertive + }; + typedef QAccessibleInterface*(*InterfaceFactory)(const QString &key, QObject*); typedef void(*UpdateHandler)(QAccessibleEvent *event); typedef void(*RootObjectHandler)(QObject*);