From 1159bb29a8c78481468c18ee218af146ff8d9d89 Mon Sep 17 00:00:00 2001 From: Paul Walker Date: Fri, 31 Jan 2025 20:37:53 -0500 Subject: [PATCH] A suite of screen reader / accessible fixes 1. PM/RM control reads with better name than "F/A". Closes #152 2. Menu buttons announce in screen reader. Closes #191 3. Tab order of new components correct. Closes #190 --- libs/sst/sst-jucegui | 2 +- src/synth/patch.h | 16 ++++++------ src/ui/dahdsr-components.h | 3 ++- src/ui/finetune-sub-panel.cpp | 6 +++++ src/ui/main-sub-panel.cpp | 6 +++++ src/ui/mainpan-sub-panel.cpp | 6 +++++ src/ui/matrix-panel.cpp | 1 + src/ui/matrix-sub-panel.cpp | 8 ++++++ src/ui/mixer-sub-panel.cpp | 8 ++++++ src/ui/modulation-components.h | 6 +++-- src/ui/playmode-sub-panel.cpp | 3 ++- src/ui/self-sub-panel.cpp | 8 ++++++ src/ui/source-sub-panel.cpp | 47 ++++++++++++++++++++++------------ src/ui/ui-constants.h | 17 ++++++++++++ 14 files changed, 108 insertions(+), 29 deletions(-) diff --git a/libs/sst/sst-jucegui b/libs/sst/sst-jucegui index 83cbb56..4e943bd 160000 --- a/libs/sst/sst-jucegui +++ b/libs/sst/sst-jucegui @@ -1 +1 @@ -Subproject commit 83cbb56eed9cf95dc27ca253d5e617adc2f2bd13 +Subproject commit 4e943bd559bbe5ef236409e60bacc60e14ca7b49 diff --git a/src/synth/patch.h b/src/synth/patch.h index 799ea06..3dcfb82 100644 --- a/src/synth/patch.h +++ b/src/synth/patch.h @@ -709,14 +709,14 @@ struct Patch : pats::PatchBase .withFlags(CLAP_PARAM_IS_STEPPED) .withDefault(false) .withID(id(1, idx))), - pmOrRM( - intMd() - .withRange(0, 1) - .withDefault(0) - .withName(name(idx) + " PM or RM") - .withGroupName(name(idx)) - .withID(id(35, idx)) - .withUnorderedMapFormatting({{0, std::string() + u8"\U000003C6"}, {1, "A"}})), + pmOrRM(intMd() + .withRange(0, 1) + .withDefault(0) + .withName(name(idx) + " PM or RM") + .withGroupName(name(idx)) + .withID(id(35, idx)) + .withUnorderedMapFormatting( + {{0, "Phase Modulation"}, {1, "Ring Modulation"}})), DAHDSRMixin(name(idx), id(2, idx), false, false, id(50, idx)), LFOMixin(name(idx), id(14, idx)), lfoToDepth(floatMd() diff --git a/src/ui/dahdsr-components.h b/src/ui/dahdsr-components.h index 838ddac..3fa2f58 100644 --- a/src/ui/dahdsr-components.h +++ b/src/ui/dahdsr-components.h @@ -246,7 +246,8 @@ template struct DAHDSRComponents {Synth::UIToAudioMsg::Action::SET_PARAM, pid, (float)(!tfz)}); w->editor.flushOperator(); }); - p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor)); + p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor), + makeMenuAccessibleButtonCB(triggerButton.get())); } }; } // namespace baconpaul::six_sines::ui diff --git a/src/ui/finetune-sub-panel.cpp b/src/ui/finetune-sub-panel.cpp index a0250c6..4ec2a9f 100644 --- a/src/ui/finetune-sub-panel.cpp +++ b/src/ui/finetune-sub-panel.cpp @@ -24,6 +24,10 @@ FineTuneSubPanel::FineTuneSubPanel(SixSinesEditor &e) : HasEditor(e) setupModulation(e, on); setupLFO(e, on); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + depTitle = std::make_unique(); depTitle->setText("Tune"); addAndMakeVisible(*depTitle); @@ -33,12 +37,14 @@ FineTuneSubPanel::FineTuneSubPanel(SixSinesEditor &e) : HasEditor(e) envDepthLL = std::make_unique(); envDepthLL->setText(std::string() + "Env " + u8"\U00002192"); addAndMakeVisible(*envDepthLL); + traverse(envDepth); createRescaledComponent(editor, *this, on.lfoDepth, lfoDep, lfoDepDA); addAndMakeVisible(*lfoDep); lfoDepL = std::make_unique(); lfoDepL->setText(std::string() + "LFO " + u8"\U00002192"); addAndMakeVisible(*lfoDepL); + traverse(lfoDep); resized(); repaint(); diff --git a/src/ui/main-sub-panel.cpp b/src/ui/main-sub-panel.cpp index 88d841c..b97a5d0 100644 --- a/src/ui/main-sub-panel.cpp +++ b/src/ui/main-sub-panel.cpp @@ -27,6 +27,10 @@ MainSubPanel::MainSubPanel(SixSinesEditor &e) setupModulation(e, on); setupLFO(e, on); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + velTitle = std::make_unique(); velTitle->setText("Mod"); addAndMakeVisible(*velTitle); @@ -36,12 +40,14 @@ MainSubPanel::MainSubPanel(SixSinesEditor &e) velSenL = std::make_unique(); velSenL->setText("Vel Sens"); addAndMakeVisible(*velSenL); + traverse(velSen); createRescaledComponent(editor, *this, on.lfoDepth, lfoDep, lfoDepDA); addAndMakeVisible(*lfoDep); lfoDepL = std::make_unique(); lfoDepL->setText(std::string() + "LFO " + u8"\U00002192"); addAndMakeVisible(*lfoDepL); + traverse(lfoDep); }; MainSubPanel::~MainSubPanel() {} diff --git a/src/ui/mainpan-sub-panel.cpp b/src/ui/mainpan-sub-panel.cpp index c88813f..c739f01 100644 --- a/src/ui/mainpan-sub-panel.cpp +++ b/src/ui/mainpan-sub-panel.cpp @@ -24,6 +24,10 @@ MainPanSubPanel::MainPanSubPanel(SixSinesEditor &e) : HasEditor(e) setupModulation(e, on); setupLFO(e, on); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + depTitle = std::make_unique(); depTitle->setText("Pan"); addAndMakeVisible(*depTitle); @@ -33,12 +37,14 @@ MainPanSubPanel::MainPanSubPanel(SixSinesEditor &e) : HasEditor(e) envDepthLL = std::make_unique(); envDepthLL->setText(std::string() + "Env " + u8"\U00002192"); addAndMakeVisible(*envDepthLL); + traverse(envDepth); createRescaledComponent(editor, *this, on.lfoDepth, lfoDep, lfoDepDA); addAndMakeVisible(*lfoDep); lfoDepL = std::make_unique(); lfoDepL->setText(std::string() + "LFO " + u8"\U00002192"); addAndMakeVisible(*lfoDepL); + traverse(lfoDep); resized(); repaint(); diff --git a/src/ui/matrix-panel.cpp b/src/ui/matrix-panel.cpp index 5dcb47c..81e4146 100644 --- a/src/ui/matrix-panel.cpp +++ b/src/ui/matrix-panel.cpp @@ -69,6 +69,7 @@ MatrixPanel::MatrixPanel(SixSinesEditor &e) : jcmp::NamedPanel("Matrix"), HasEdi createComponent(editor, *this, mx[i].pmOrRM, Mpmrm[i], MpmrmD[i], i, false); Mpmrm[i]->direction = sst::jucegui::components::MultiSwitch::HORIZONTAL; + Mpmrm[i]->setAbbreviatedLabelMap({{0, std::string() + u8"\U000003C6"}, {1, "A"}}); addAndMakeVisible(*Mpmrm[i]); auto si = MatrixIndex::sourceIndexAt(i); diff --git a/src/ui/matrix-sub-panel.cpp b/src/ui/matrix-sub-panel.cpp index 521bfd1..8ea166f 100644 --- a/src/ui/matrix-sub-panel.cpp +++ b/src/ui/matrix-sub-panel.cpp @@ -31,20 +31,27 @@ void MatrixSubPanel::setSelectedIndex(int idx) setupLFO(editor, m); setupModulation(editor, m); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + createRescaledComponent(editor, *this, m.lfoToDepth, lfoToDepth, lfoToDepthDA); addAndMakeVisible(*lfoToDepth); lfoToDepthL = std::make_unique(); lfoToDepthL->setText("Depth"); addAndMakeVisible(*lfoToDepthL); + traverse(lfoToDepth); createRescaledComponent(editor, *this, m.envToLevel, envToLev, envToLevDA); addAndMakeVisible(*envToLev); envToLevL = std::make_unique(); envToLevL->setText("Level"); addAndMakeVisible(*envToLevL); + traverse(envToLev); createComponent(editor, *this, m.envIsMultiplcative, envMul, envMulD); addAndMakeVisible(*envMul); + traverse(envMul); modLabelE = std::make_unique(); modLabelE->setText(std::string() + "Env" + u8"\U00002192"); @@ -60,6 +67,7 @@ void MatrixSubPanel::setSelectedIndex(int idx) createComponent(editor, *this, m.overdrive, overdrive, overdriveD); addAndMakeVisible(*overdrive); overdrive->setLabel("10x"); + traverse(overdrive); auto op = [w = juce::Component::SafePointer(this)]() { diff --git a/src/ui/mixer-sub-panel.cpp b/src/ui/mixer-sub-panel.cpp index ceb4929..5a49856 100644 --- a/src/ui/mixer-sub-panel.cpp +++ b/src/ui/mixer-sub-panel.cpp @@ -32,26 +32,34 @@ void MixerSubPanel::setSelectedIndex(int idx) setupLFO(editor, sn); setupModulation(editor, sn); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + createRescaledComponent(editor, *this, sn.lfoToLevel, lfoToLevel, lfoToLevelDA); addAndMakeVisible(*lfoToLevel); lfoToLevelL = std::make_unique(); lfoToLevelL->setText("Level"); addAndMakeVisible(*lfoToLevelL); + traverse(lfoToLevel); createRescaledComponent(editor, *this, sn.lfoToPan, lfoToPan, lfoToPanDA); addAndMakeVisible(*lfoToPan); lfoToPanL = std::make_unique(); lfoToPanL->setText("Pan"); addAndMakeVisible(*lfoToPanL); + traverse(lfoToPan); createRescaledComponent(editor, *this, sn.envToLevel, envToLev, envToLevDA); addAndMakeVisible(*envToLev); envToLevL = std::make_unique(); envToLevL->setText("Level"); addAndMakeVisible(*envToLevL); + traverse(envToLev); createComponent(editor, *this, sn.envIsMultiplcative, envMul, envMulD); addAndMakeVisible(*envMul); + traverse(envMul); modLabelE = std::make_unique(); modLabelE->setText(std::string() + "Env" + u8"\U00002192"); diff --git a/src/ui/modulation-components.h b/src/ui/modulation-components.h index a7ea6a8..99001f2 100644 --- a/src/ui/modulation-components.h +++ b/src/ui/modulation-components.h @@ -198,7 +198,8 @@ template struct ModulationComponents }); } } - p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor)); + p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor), + makeMenuAccessibleButtonCB(targetMenu[index].get())); } void showSourceMenu(int index) { @@ -243,7 +244,8 @@ template struct ModulationComponents { p.addSubMenu(currCat, s); } - p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor)); + p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&asComp()->editor), + makeMenuAccessibleButtonCB(sourceMenu[index].get())); } std::unique_ptr modTitleLab; diff --git a/src/ui/playmode-sub-panel.cpp b/src/ui/playmode-sub-panel.cpp index 6764ac2..3c8ebf3 100644 --- a/src/ui/playmode-sub-panel.cpp +++ b/src/ui/playmode-sub-panel.cpp @@ -302,7 +302,8 @@ void PlayModeSubPanel::showTriggerButtonMenu() (float)(!atfl)}); }); - p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&this->editor)); + p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&this->editor), + makeMenuAccessibleButtonCB(triggerButton.get())); } void PlayModeSubPanel::setEnabledState() diff --git a/src/ui/self-sub-panel.cpp b/src/ui/self-sub-panel.cpp index 13f107a..8347c71 100644 --- a/src/ui/self-sub-panel.cpp +++ b/src/ui/self-sub-panel.cpp @@ -32,20 +32,27 @@ void SelfSubPanel::setSelectedIndex(int idx) setupLFO(editor, editor.patchCopy.selfNodes[idx]); setupModulation(editor, editor.patchCopy.selfNodes[idx]); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + createRescaledComponent(editor, *this, n.lfoToFB, lfoToFb, lfoToFbDA); addAndMakeVisible(*lfoToFb); lfoToFbL = std::make_unique(); lfoToFbL->setText("Depth"); addAndMakeVisible(*lfoToFbL); + traverse(lfoToFb); createRescaledComponent(editor, *this, n.envToFB, envToLev, envToLevDA); addAndMakeVisible(*envToLev); envToLevL = std::make_unique(); envToLevL->setText("Level"); addAndMakeVisible(*envToLevL); + traverse(envToLev); createComponent(editor, *this, n.envIsMultiplcative, envMul, envMulD); addAndMakeVisible(*envMul); + traverse(envMul); modLabelE = std::make_unique(); modLabelE->setText(std::string() + "Env" + u8"\U00002192"); @@ -61,6 +68,7 @@ void SelfSubPanel::setSelectedIndex(int idx) createComponent(editor, *this, n.overdrive, overdrive, overdriveD); addAndMakeVisible(*overdrive); overdrive->setLabel("10x"); + traverse(overdrive); auto op = [w = juce::Component::SafePointer(this)]() { diff --git a/src/ui/source-sub-panel.cpp b/src/ui/source-sub-panel.cpp index 31495f5..ab3e3d5 100644 --- a/src/ui/source-sub-panel.cpp +++ b/src/ui/source-sub-panel.cpp @@ -68,6 +68,10 @@ void SourceSubPanel::setSelectedIndex(size_t idx) setupLFO(editor, sn); setupModulation(editor, sn); + auto travidx{400}; + auto traverse = [&travidx](auto &c) + { sst::jucegui::component_adapters::setTraversalId(c.get(), travidx++); }; + createComponent(editor, *this, sn.envToRatio, envToRatio, envToRatioD); createComponent(editor, *this, sn.envToRatioFine, envToRatioFine, envToRatioFineD); envToRatioL = std::make_unique(); @@ -78,6 +82,8 @@ void SourceSubPanel::setSelectedIndex(size_t idx) addAndMakeVisible(*envToRatio); addAndMakeVisible(*envToRatioFine); addAndMakeVisible(*envToRatioFineL); + traverse(envToRatio); + traverse(envToRatioFine); createComponent(editor, *this, sn.lfoToRatio, lfoToRatio, lfoToRatioD); createComponent(editor, *this, sn.lfoToRatioFine, lfoToRatioFine, lfoToRatioFineD); @@ -89,6 +95,8 @@ void SourceSubPanel::setSelectedIndex(size_t idx) addAndMakeVisible(*lfoToRatio); addAndMakeVisible(*lfoToRatioFine); addAndMakeVisible(*lfoToRatioFineL); + traverse(lfoToRatio); + traverse(lfoToRatioFine); modTitle = std::make_unique(); modTitle->setText("Env Depth"); @@ -102,6 +110,9 @@ void SourceSubPanel::setSelectedIndex(size_t idx) wavTitle->setText("Wave"); addAndMakeVisible(*wavTitle); + wavPainter = std::make_unique(sn.waveForm, sn.startingPhase); + addAndMakeVisible(*wavPainter); + createComponent(editor, *this, sn.waveForm, wavButton, wavButtonD); addAndMakeVisible(*wavButton); wavButtonD->onGuiSetValue = [this]() @@ -109,9 +120,21 @@ void SourceSubPanel::setSelectedIndex(size_t idx) wavPainter->repaint(); wavButton->repaint(); }; + traverse(wavButton); - wavPainter = std::make_unique(sn.waveForm, sn.startingPhase); - addAndMakeVisible(*wavPainter); + createComponent(editor, *this, sn.startingPhase, startingPhase, startingPhaseD); + addAndMakeVisible(*startingPhase); + traverse(startingPhase); + startingPhaseD->onGuiSetValue = [w = juce::Component::SafePointer(this)]() + { + if (!w) + return; + w->wavPainter->repaint(); + }; + + startingPhaseL = std::make_unique(); + startingPhaseL->setText(std::string() + u8"\U000003C6"); + addAndMakeVisible(*startingPhaseL); keyTrackTitle = std::make_unique(); keyTrackTitle->setText("Pitch"); @@ -120,25 +143,14 @@ void SourceSubPanel::setSelectedIndex(size_t idx) createComponent(editor, *this, sn.keyTrack, keyTrack, keyTrackD); keyTrack->setLabel("KeyTrak"); addAndMakeVisible(*keyTrack); + traverse(keyTrack); createComponent(editor, *this, sn.keyTrackValue, keyTrackValue, keyTrackValueD); addAndMakeVisible(*keyTrackValue); keyTrackValueLL = std::make_unique(); keyTrackValueLL->setText("f"); addAndMakeVisible(*keyTrackValueLL); - - createComponent(editor, *this, sn.startingPhase, startingPhase, startingPhaseD); - addAndMakeVisible(*startingPhase); - startingPhaseD->onGuiSetValue = [w = juce::Component::SafePointer(this)]() - { - if (!w) - return; - w->wavPainter->repaint(); - }; - - startingPhaseL = std::make_unique(); - startingPhaseL->setText(std::string() + u8"\U000003C6"); - addAndMakeVisible(*startingPhaseL); + traverse(keyTrackValue); auto op = [w = juce::Component::SafePointer(this)]() { @@ -150,6 +162,7 @@ void SourceSubPanel::setSelectedIndex(size_t idx) createComponent(editor, *this, sn.octTranspose, tsposeButton, tsposeButtonD); addAndMakeVisible(*tsposeButton); + traverse(tsposeButton); tsposeButtonL = std::make_unique(); tsposeButtonL->setText("Octave"); @@ -164,6 +177,7 @@ void SourceSubPanel::setSelectedIndex(size_t idx) w->showUnisonFeaturesMenu(); }); addAndMakeVisible(*unisonBehaviorB); + traverse(unisonBehaviorB); setEnabledState(); @@ -322,7 +336,8 @@ void SourceSubPanel::showUnisonFeaturesMenu() w->editor.sendParamSetValue(u2oid, 2); }); - p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&editor)); + p.showMenuAsync(juce::PopupMenu::Options().withParentComponent(&editor), + makeMenuAccessibleButtonCB(unisonBehaviorB.get())); } } // namespace baconpaul::six_sines::ui \ No newline at end of file diff --git a/src/ui/ui-constants.h b/src/ui/ui-constants.h index 67dcac5..7b8d5b2 100644 --- a/src/ui/ui-constants.h +++ b/src/ui/ui-constants.h @@ -143,5 +143,22 @@ inline sst::jucegui::layouts::LayoutComponent labelKnobLayout(const K &k, const return res; } +inline std::function makeMenuAccessibleButtonCB(juce::Component *c) +{ + return [w = juce::Component::SafePointer(c)](int) + { + if (w) + { + w->grabKeyboardFocus(); + auto h = w->getAccessibilityHandler(); + if (h) + { + h->grabFocus(); + h->notifyAccessibilityEvent(juce::AccessibilityEvent::titleChanged); + } + } + }; +} + } // namespace baconpaul::six_sines::ui #endif // UI_CONSTANTS_H