Skip to content

Accessibility: Make screen readers announce UI actions #28592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

shoogle
Copy link
Contributor

@shoogle shoogle commented Jun 26, 2025

Resolves:

Adds function AccessibilityController::announce(QString) that can be used to make the screen reader speak an arbitrary message not covered by standard accessibility events.

The announce() function can be used to report that an action was performed, a different mode was entered, or a change occurred to an item that isn't the current focus item.

Important

The announce() function shouldn't be (ab)used to report standard accessibility event types covered by QAccessibleEvent and its subclasses, such as changes in focus, or changes to the value, state, or text property of the focus item, unless such events are ignored or misreported by a particular screen reader.

Example annoucements:

  • "Bold face" or "Italics" when you press Ctrl+B or Ctrl+I while editing a text element in the score.
  • "Staccato" when you press Shift+S while a note is selected.
  • "Up", "Down", "Shift text left", or "Shift text right" when you nudge a moveable element with the arrow keys.
  • "6-key input mode" or "Navigation mode" when you press N in the Braille panel.
  • "Input by note name mode" or "Input by duration mode" when you press N or M in the score.
  • "Voice 1" to "Voice 4" when you press Ctrl+Alt+1 to 4 in note input mode.

Some things could be improved, but these are left for a future PR:

  • Toggleable actions like bold, italics, and staccato ought to say "on" or "off" so you know what the new state is.
  • Directional nudges could state the distance from the default position (e.g. "3sp above the default position").

For clues implementing announcements, see how most announcements come from UI Action titles, but this can be overriden for a particular action by setting a custom announcement, as is done with the note input actions.

@shoogle shoogle force-pushed the accessibility-announcements branch 2 times, most recently from 9fc7a1a to c27a41a Compare July 1, 2025 05:06
@shoogle shoogle force-pushed the accessibility-announcements branch 2 times, most recently from 38ed158 to 28b7f50 Compare July 17, 2025 10:42
@shoogle shoogle force-pushed the accessibility-announcements branch 9 times, most recently from fba2c32 to 86c6ae6 Compare July 30, 2025 04:04
@shoogle shoogle marked this pull request as ready for review July 30, 2025 04:24
@shoogle shoogle requested a review from Eism July 30, 2025 21:30
@shoogle shoogle marked this pull request as draft August 6, 2025 18:54
@shoogle shoogle force-pushed the accessibility-announcements branch 3 times, most recently from 1efab15 to 5a1baac Compare August 7, 2025 20:37
@shoogle shoogle marked this pull request as ready for review August 7, 2025 21:56
@shoogle shoogle requested review from mathesoncalum and removed request for Eism August 8, 2025 08:29
@shoogle shoogle force-pushed the accessibility-announcements branch from 5a1baac to 5f2e76c Compare August 18, 2025 10:58
@shoogle shoogle marked this pull request as draft August 18, 2025 15:32
@shoogle shoogle force-pushed the accessibility-announcements branch 2 times, most recently from 55374e7 to 751326a Compare August 21, 2025 03:14
Comment on lines +110 to +114
dispatcher->preDispatch().onReceive(this, [this](const actions::ActionCode*) {
// About to perform an action. Let's store some values to see if the action changes them.
m_preDispatchFocus = m_lastFocused;
m_preDispatchName = m_preDispatchFocus ? m_preDispatchFocus->accessibleName() : QString();
m_announcement.clear(); // So we can detect if the action sets its own announcement.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igorkorsukov, are channel onReceive functions always called synchronously, or is there a risk that the action was already performed by the time this code executes?

The idea is to store some values before the action is performed, then check them afterwards to see if they were altered by the action.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the sender and receiver are in the same thread, then always synchronously

@shoogle shoogle force-pushed the accessibility-announcements branch from 751326a to e381d40 Compare August 21, 2025 13:08
@shoogle shoogle force-pushed the accessibility-announcements branch from e381d40 to 623c539 Compare August 21, 2025 13:29
@shoogle shoogle marked this pull request as ready for review August 21, 2025 14:27
Create function AccessibilityController::announce(QString) that makes
the screen reader speak an arbitrary message. Unlike conventional
screen reader speech, announcements are not limited to reporting that
an item has gained focus, or that one of the focus item's attributes
has changed (although the function works by pretending that at least
one of those events has occurred).
Fix musescore#20041 No screen reader feedback when performing in-score actions
Fix musescore#20000 Change of voice not announced by screen reader
Override the normal action announcement because these are toggling
actions, so the action name is always the same regardless of whether
you are entering or exiting the mode.
PR musescore#27776 restricted it so the score had to have focus, but there
are a couple of other places where it should work too.
AccessibleRoot::commandInfo() only worked in the score whereas
AccessibilityController::announce() works in all areas of the UI.
@shoogle shoogle force-pushed the accessibility-announcements branch from 623c539 to c859cb1 Compare August 21, 2025 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants