Skip to content

Commit

Permalink
Matrix background click. Accessible key bindings (#101)
Browse files Browse the repository at this point in the history
1. Matrix lets you click in background to select a node. Way
   better

2. Added accessible actions CmdA and CmdJ to arm and jump to a
   region and documented. Added hook for CmdN to navigate but didn't
   do that yet. Once I do we are ready for 1.0
  • Loading branch information
baconpaul authored Jan 7, 2025
1 parent ea8edea commit fa3d402
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 29 deletions.
25 changes: 25 additions & 0 deletions doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,31 @@ Press the COG icon at the top of the UI to get the settings
screen, where you can control MPE, Mono mode, pitch bend depth,
and more.

## Screen Reader and Accessible Support

Six Sines supports screen readers and accessible gestures, making
the UI and programming model as inscrutable to these assistive technologies
as it is to users with a visual display. Since the UI is quite big there's
a few extra features for screen reader navigation.

First, standard edit gestures should work on all controls, and I tried
really hard to make sure tab order makes sense and labels are reasonable. If
you find one which is wrong, please just drop a note on discord or github.

The structure of the UI is that knbos (like "Op3 feedback level") a panel
in the bottom 1/4 of the screen to edit the modulators and stuff. This panel
arrives when you mouse click or edit the knob. A few features make this
easier to navigate for a screen reader.

If on a knob in the top third, `Command-A' will arm that knob (namely select the
knob modulation panel in the area below)

If on a knob in the top third, `Command-J` will jump to the control panel in the
bottom

And finally from anywhere in the UI, `Command-N` will expose a menu allowing
you to focus any of the focusable top-section knobs or preset manager.

## Good Luck, and..

Good luck! Its fun. But tricky. If you want to add to this manual
Expand Down
2 changes: 1 addition & 1 deletion libs/sst/sst-jucegui
6 changes: 6 additions & 0 deletions src/ui/knob-highlight.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ namespace baconpaul::six_sines::ui
{
struct KnobHighlight : public juce::Component
{
KnobHighlight()
{
setAccessible(false);
setWantsKeyboardFocus(false);
setInterceptsMouseClicks(false, false);
}
void paint(juce::Graphics &g) override
{
g.setColour(juce::Colour(0xFF * 0.3, 0x90 * 0.3, 00));
Expand Down
18 changes: 18 additions & 0 deletions src/ui/main-panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,24 @@ void MainPanel::resized()
// voiceLimit->setBounds(vb);
}

void MainPanel::mouseDown(const juce::MouseEvent &e)
{
for (int i = 0; i < numOps; ++i)
{
if (rectangleFor(i).contains(e.position.toInt()))
{
beginEdit(i);
}
}
}

juce::Rectangle<int> MainPanel::rectangleFor(int idx)
{
auto b = getContentArea().reduced(uicMargin, 0);
return juce::Rectangle<int>(b.getX() + idx * uicKnobSize + (idx ? idx - 0.5 : 0) * uicMargin,
b.getY(), uicKnobSize + uicMargin, uicLabeledKnobHeight);
}

void MainPanel::beginEdit(int which)
{
if (which == 3)
Expand Down
3 changes: 3 additions & 0 deletions src/ui/main-panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ struct MainPanel : jcmp::NamedPanel, HasEditor

void resized() override;

void mouseDown(const juce::MouseEvent &e) override;
juce::Rectangle<int> rectangleFor(int idx);

void beginEdit(int which);

std::unique_ptr<juce::Component> highlight;
Expand Down
65 changes: 44 additions & 21 deletions src/ui/matrix-panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,47 @@ void MatrixPanel::paint(juce::Graphics &g)
}
}

void MatrixPanel::mouseDown(const juce::MouseEvent &e)
{
for (int i = 0; i < numOps; ++i)
{
if (rectangleFor(i, true).contains(e.position.toInt()))
{
beginEdit(i, true);
return;
}
}

for (int i = 0; i < matrixSize; ++i)
{
if (rectangleFor(i, false).contains(e.position.toInt()))
{
beginEdit(i, false);
return;
}
}
}

juce::Rectangle<int> MatrixPanel::rectangleFor(int idx, bool self)
{
auto b = getContentArea().reduced(uicMargin, 0);

if (self)
{
return juce::Rectangle<int>(b.getX() + idx * (uicPowerKnobWidth + uicMargin),
b.getY() + idx * (uicLabeledKnobHeight + uicMargin),
uicPowerKnobWidth + 2, uicLabeledKnobHeight);
}
else
{
auto si = MatrixIndex::sourceIndexAt(idx);
auto ti = MatrixIndex::targetIndexAt(idx);
auto y = b.getY() + ti * (uicLabeledKnobHeight + uicMargin);
auto x = b.getX() + si * (uicPowerKnobWidth + uicMargin);
return juce::Rectangle<int>(x, y, uicPowerKnobWidth + 2, uicLabeledKnobHeight);
}
}

void MatrixPanel::beginEdit(size_t idx, bool self)
{
editor.hideAllSubPanels();
Expand All @@ -182,27 +223,9 @@ void MatrixPanel::beginEdit(size_t idx, bool self)
editor.matrixSubPanel->setSelectedIndex(idx);
}

if (self)
{
highlight->setVisible(true);
auto b = getContentArea().reduced(uicMargin, 0);
highlight->setBounds(b.getX() + idx * (uicPowerKnobWidth + uicMargin),
b.getY() + idx * (uicLabeledKnobHeight + uicMargin),
uicPowerKnobWidth + 2, uicLabeledKnobHeight);
highlight->toBack();
}
else
{
highlight->setVisible(true);
auto b = getContentArea().reduced(uicMargin, 0);
auto si = MatrixIndex::sourceIndexAt(idx);
auto ti = MatrixIndex::targetIndexAt(idx);
auto y = b.getY() + ti * (uicLabeledKnobHeight + uicMargin);
auto x = b.getX() + si * (uicPowerKnobWidth + uicMargin);

highlight->setBounds(x, y, uicPowerKnobWidth + 2, uicLabeledKnobHeight);
highlight->toBack();
}
highlight->setVisible(true);
highlight->setBounds(rectangleFor(idx, self));
highlight->toBack();
}

} // namespace baconpaul::six_sines::ui
3 changes: 3 additions & 0 deletions src/ui/matrix-panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ struct MatrixPanel : jcmp::NamedPanel, HasEditor

void beginEdit(size_t, bool);

void mouseDown(const juce::MouseEvent &e) override;
juce::Rectangle<int> rectangleFor(int idx, bool self);

std::unique_ptr<juce::Component> highlight;
void clearHighlight()
{
Expand Down
23 changes: 20 additions & 3 deletions src/ui/mixer-panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ void MixerPanel::resized()
}
}

void MixerPanel::mouseDown(const juce::MouseEvent &e)
{
for (int i = 0; i < numOps; ++i)
{
if (rectangleFor(i).contains(e.position.toInt()))
{
beginEdit(i);
}
}
}

juce::Rectangle<int> MixerPanel::rectangleFor(int idx)
{
auto b = getContentArea().reduced(uicMargin, 0);
return juce::Rectangle<int>(b.getX(), b.getY() + idx * (uicLabeledKnobHeight + uicMargin),
uicPowerKnobWidth + uicMargin + uicKnobSize + 2,
uicLabeledKnobHeight);
}

void MixerPanel::beginEdit(size_t idx)
{
editor.hideAllSubPanels();
Expand All @@ -90,9 +109,7 @@ void MixerPanel::beginEdit(size_t idx)
editor.singlePanel->setName("Op " + std::to_string(idx + 1) + " Mix");

highlight->setVisible(true);
auto b = getContentArea().reduced(uicMargin, 0);
highlight->setBounds(b.getX(), b.getY() + idx * (uicLabeledKnobHeight + uicMargin),
uicPowerKnobWidth + uicMargin + uicKnobSize + 2, uicLabeledKnobHeight);
highlight->setBounds(rectangleFor(idx));
highlight->toBack();
}

Expand Down
3 changes: 3 additions & 0 deletions src/ui/mixer-panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ struct MixerPanel : jcmp::NamedPanel, HasEditor
highlight->setVisible(false);
}

void mouseDown(const juce::MouseEvent &e) override;
juce::Rectangle<int> rectangleFor(int idx);

std::array<std::unique_ptr<jcmp::Knob>, numOps> knobs;
std::array<std::unique_ptr<PatchContinuous>, numOps> knobsData;
std::array<std::unique_ptr<jcmp::ToggleButton>, numOps> power;
Expand Down
2 changes: 2 additions & 0 deletions src/ui/patch-data-bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ void createComponent(SixSinesEditor &e, P &panel, const Param &parm, std::unique
cm->setSource(pc.get());

e.componentByID[id] = juce::Component::SafePointer<juce::Component>(cm.get());
e.panelSelectGestureFor[cm.get()] = [args..., &panel]() { panel.beginEdit(args...); };
}

template <typename P, typename T, typename Rescaler, typename... Args>
Expand Down Expand Up @@ -234,6 +235,7 @@ void createRescaledComponent(SixSinesEditor &e, P &panel, const Param &parm, std
cm->setSource(rc.get());

e.componentByID[id] = juce::Component::SafePointer<juce::Component>(cm.get());
e.panelSelectGestureFor[cm.get()] = [args..., &panel]() { panel.beginEdit(args...); };
}

} // namespace baconpaul::six_sines::ui
Expand Down
19 changes: 18 additions & 1 deletion src/ui/six-sines-editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,28 @@ void SixSinesEditor::postPatchChange(const std::string &dn)

bool SixSinesEditor::keyPressed(const juce::KeyPress &key)
{
if (key.getModifiers().isCommandDown() && (char)key.getKeyCode() == 78)
if (key.getModifiers().isCommandDown() && (char)key.getKeyCode() == 'N')
{
SXSNLOG("Navigation Menu");
return true;
}

if (key.getModifiers().isCommandDown() && (char)key.getKeyCode() == 'A')
{
auto c = getCurrentlyFocusedComponent();
auto psgf = panelSelectGestureFor.find(c);

if (psgf != panelSelectGestureFor.end())
psgf->second();
return true;
}

if (key.getModifiers().isCommandDown() && (char)key.getKeyCode() == 'J')
{
singlePanel->grabKeyboardFocus();
return true;
}

return false;
}

Expand Down
2 changes: 2 additions & 0 deletions src/ui/six-sines-editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ struct SixSinesEditor : jcmp::WindowPanel
void visibilityChanged() override;
void parentHierarchyChanged() override;
std::unique_ptr<sst::jucegui::accessibility::FocusDebugger> focusDebugger;

std::unordered_map<juce::Component *, std::function<void()>> panelSelectGestureFor;
};

struct HasEditor
Expand Down
22 changes: 19 additions & 3 deletions src/ui/source-panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ void SourcePanel::resized()
}
}

void SourcePanel::mouseDown(const juce::MouseEvent &e)
{
for (int i = 0; i < numOps; ++i)
{
if (rectangleFor(i).contains(e.position.toInt()))
{
beginEdit(i);
}
}
}

juce::Rectangle<int> SourcePanel::rectangleFor(int idx)
{
auto b = getContentArea().reduced(uicMargin, 0);
return juce::Rectangle<int>(b.getX() + idx * (uicPowerKnobWidth + uicMargin), b.getY(),
uicPowerKnobWidth + 2, uicLabeledKnobHeight);
}

void SourcePanel::beginEdit(size_t idx)
{
editor.hideAllSubPanels();
Expand All @@ -70,9 +88,7 @@ void SourcePanel::beginEdit(size_t idx)
editor.singlePanel->setName("Op " + std::to_string(idx + 1) + " Source");

highlight->setVisible(true);
auto b = getContentArea().reduced(uicMargin, 0);
highlight->setBounds(b.getX() + idx * (uicPowerKnobWidth + uicMargin), b.getY(),
uicPowerKnobWidth + 2, uicLabeledKnobHeight);
highlight->setBounds(rectangleFor(idx));
highlight->toBack();
}

Expand Down
3 changes: 3 additions & 0 deletions src/ui/source-panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ struct SourcePanel : jcmp::NamedPanel, HasEditor

void beginEdit(size_t idx);

void mouseDown(const juce::MouseEvent &e) override;
juce::Rectangle<int> rectangleFor(int idx);

std::unique_ptr<juce::Component> highlight;
void clearHighlight()
{
Expand Down

0 comments on commit fa3d402

Please sign in to comment.