I tested our app with JAWS today, which I’m told is the most popular screen reader app. It works as expected with the new JUCE 6.1 accessibility features (which are great, btw), but there’s a problem.
When a button is focused, JAWS insists on telling users “To activate press spacebar”. That’s fine in a normal Windows dialog box, but space doesn’t activate JUCE buttons!
The Microsoft “Guidelines for Keyboard User Interface Design” state:
In Windows applications, users navigate by pressing the TAB key to move the input focus from one UI element to another. They press the SPACEBAR or ENTER key to choose the currently selected active region or to activate a control or command.
It seems like something that should be fixed on the JUCE side. Is there anything that can be done about it?
More issues. Decided to post everything here, but happy to break out into separate threads if necessary. All of the following occurs on Windows. Haven’t tested on Mac yet. I’m mostly using the free NVDA screen reader since JAWS only has a 40 minute free trial.
Issue #2 AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent::valueChanged) is currently a no-op in juce_win32_Accessibility.cpp. It’s not possible to notify accessibility clients that a UI elements value has changed. Adding the following code gets it working:
Issue 3 juce::ComboBox doesn’t notify accessibility clients when the selected item changes via keyboard (up/down/left/right). As a result, screen readers don’t inform the user what the new value is. Adding the following code to the bottom of juce::ComboBox::setSelectedId() gets it working (assuming issue 2 is fixed as above):
if (auto handler = getAccessibilityHandler())
handler->notifyAccessibilityEvent (AccessibilityEvent::valueChanged);
Edit: I forgot I also had to make ComboBoxAccessibilityHandler implement AccessibilityTextValueInterface.
Issue #4 juce::Button::setToggledState() calls notifyAccessibilityEvent (AccessibilityEvent::valueChanged), but ButtonAccessibilityHandler doesn’t implement AccessibilityValueInterface or AccessibilityTextValueInterface, so it’s effectively a no-op. Seems like an oversight.
Wish I’d had time to run through this during the JUCE 6.1 beta phase (unfortunately, other projects took precedence), but it’s otherwise a pretty solid first version. I’ll keep testing and report back here.
Thanks! These all seem like good suggestions, I’m testing them at the moment and will hopefully get something onto develop soon.
I’m hesitant to change the default key trigger behaviour for all JUCE buttons though as it will be a silent change that may cause issues in unaware JUCE apps. There is a Button::addShortcut() method if you want to add this behaviour for your buttons though, so you can do the following:
I totally understand your reluctance to change default key trigger behaviour. It would be risky to do so given all the JUCE apps and plugins that are already out there.
That said, calling addShortcut() on every button in the app would be a major pain, and easy to forget going forward.
Would adding some kind of opt-in mechanism be possible? Maybe a static Button method or even a #define of some kind so it can be enabled app wide in one fell swoop.
As a workaround, you could derive your own classes from the Button types that you use (such as ImageButton or whatever), make your buttons use those derived classes instead, and call addShortcut() from their constructors. We do something similar to implement AAX highlighting of our controls.
I’d be hesitant to add something like that to the library since it can be done quite easily with a free function in user code with something like the following:
@ed95
Unfortunately, some of the component accessibility classes are awkward to re-use in custom components that inherit from one of the JUCE components.
For example, we have our own Button class which inherits from juce::Button. We need to make it assume different accessibility roles (normal, toggle, etc) depending on how it’s being used.
Under normal circumstances, I would just do something like this:
But I can’t access juce::ButtonAccessibilityHandler in my code because it’s defined in juce_Button.cpp (not juce_Button.h). This pattern is used elsewhere in JUCE too.
So I’m forced to duplicate the entire juce::ButtonAccessibilityHandler class, which isn’t an insignificant amount of code. And obviously my duplicate version won’t get automatically updated should you guys decide to change it in future.
Can you suggest any workarounds? If not, is there any chance the accessibility handler classes could be moved into component header files instead?
I have another issue to report on Windows. While traversing between accessible components, an extra TAB key press is required for each non-accessible component “along the way”. This is not a big deal if you only have one component that is not accessible, however in my case I have rows of not visible/accessible components which require a significant amount of TAB key presses in order to get to the next accessible component. This problem doesn’t appear on MacOs.
I have another issue to report on Windows. While traversing between accessible components, an extra TAB key press is required for each non-accessible component “along the way”. This is not a big deal if you only have one component that is not accessible, however in my case I have rows of not visible/accessible components which require a significant amount of TAB key presses in order to get to the next accessible component. This problem doesn’t appear on MacOs.
Thanks
Setting Narrator to scan mode (narrator + space bar) seems to be fixing the issue.