Skip to content
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

Matrix background click. Accessible key bindings #101

Merged
merged 1 commit into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading