fix: report Range control values to screen readers on Windows - #269
omaribrahimsaad wants to merge 2 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe accessibility item interface now handles Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The accessibility change has no confirmed merge-blocking issue. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
I've pushed a second commit replacing the Slider exclusion with the EditableText allowlist from #284, having tested it rather than guessing. Windows 10 22H2, NVDA 2026.2, Audacity 4 at ad2ad6b: buttons, checkboxes and spin boxes no longer report IsTextPatternAvailable and no longer read "blank" on arrow keys. Text fields are unaffected — character review, selection announcements and cursor reporting all still work. @kryksyh this changes what you approved, so it needs another look. Separately, a consequence of exposing the value that belongs on the Audacity side. Controls that already carry their value in accessible.name now announce it twice, and because changing the value changes the name, needsRevoicing() fires and triggerRevoicing() bounces focus to a sibling on every keypress: Removing the value from the name fixes both: That's 23 controls in Audacity and I'll open that PR there. The same pattern exists here at TextInputField.qml:113, where a field's accessible name is its own text, so every keystroke bounces focus to a sibling — I'll file that separately. |
|
@omaribrahimsaad Thanks for the editable text allow list. While this change is definitely necessary from the framework side of things (it is the same change I proposed in #284), it will regress MuseScore when not handled carefully, in the same way as nvaccess/nvda#20825. The score view in MuseScore is a tree hierarchy of text nodes: page > system > measure > segment > chord > note. From the NVDA side of things, this is explainable. NVDA treats a static text node as content when the control has text (i.e. a label). When it doesn't have text it is treated as layout. New layout nodes in the focus ancestry are ignored for announcements, new content nodes are not. Note that Static text is a leaf control type; it is never meant to contain other static text. MuseScore already models the containers correctly: So I think the right fix, alongside this PR, is on the role side:
Without that, merging this PR as is will trade "blank" on every arrow key for a burst of container names on every measure change. |
Resolves: audacity/audacity#11600
On Windows, no Range control in a muse-based application conveys its value to
screen readers. The control is reachable and interactive, but the value is never
announced — on focus NVDA reads " slider" with no value, and on each
Up/Down it reads "blank". Reproduced in Audacity 4 and in shipped MuseScore
Studio 4.7.4.
Two causes, both in
accessibleiteminterface.cpp:1.
text()never handledQAccessible::Value. Qt reads a control's valuethrough
text(QAccessible::Value)(QWindowsUiaValueProvider::get_Value).text()handledName,AcceleratorandDescription, then fell through todefault: break;returning an emptyQString— the emptyValue.Value. Thevalue data was always correct:
AccessibleItem::accessibleValue()returns validdoubles on every query, and
AccessibilityController::propertyChanged()alreadysends
QAccessibleValueChangeEventwith the real value. Only the read path Qtactually queries was missing. Qt Widgets does the equivalent in
QAccessibleSlider::text().2.
interface_castadvertisedTextInterfacefor every role, so slidersexposed UIA
TextPattern. Screen readers then read the (empty) text contentinstead of the value on value-change events. Scoped to exclude
QAccessible::Slider.Before (inspect.exe, Audacity 4 track Pan slider):
After:
RangeValuePatternis untouched, so clients reading it (JAWS, Narrator) areunaffected. Verified text fields, buttons, checkboxes and spin boxes still
announce correctly.
Note: the
TextInterfacecast carries the comment "Without Action and Textinterfaces NVDA doesn't work", so it was added deliberately. I've only excluded
Sliderand saw no regressions in testing, but if that cast is load-bearing fora case I haven't hit, I'm happy to narrow the condition further.
Related: QTBUG-139296 covers the same symptom in stock Qt Quick Controls, which
this change works around rather than depends on.
Tested on Windows 10 22H2, Qt 6.10.3, NVDA 2026.2.
Build configuration
audacity: audacity/audacity/master
audacity platforms: windows_x64
musescore: musescore/MuseScore/main
musescore platforms: windows_x64