From 2cef88fa1f5f9203be11b39698ad51add4d45054 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Fri, 23 Aug 2024 14:56:29 -0400 Subject: [PATCH] WIP CartManager acessibility --- Source/CMakeLists.txt | 2 +- Source/CartManager.cpp | 12 +- Source/GlobalEditor.cpp | 2 + Source/OperatorEditor.cpp | 8 +- Source/OperatorEditor.h | 1 + Source/PluginEditor.cpp | 34 +++- Source/PluginEditor.h | 4 +- Source/PluginProcessor.cpp | 1 + Source/ProgramListBox.cpp | 335 ++++++++++++++++++++++++++----------- Source/ProgramListBox.h | 35 ++-- 10 files changed, 299 insertions(+), 135 deletions(-) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index db1ae900..a8313d1a 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -21,7 +21,7 @@ juce_add_plugin("${BaseTargetName}" NEEDS_MIDI_INPUT TRUE NEEDS_MIDI_OUTPUT TRUE IS_MIDI_EFFECT FALSE - EDITOR_WANTS_KEYBOARD_FOCUS FALSE + EDITOR_WANTS_KEYBOARD_FOCUS TRUE PLUGIN_MANUFACTURER_CODE DGSB PLUGIN_CODE Dexd FORMATS ${DEXED_JUCE_FORMATS} diff --git a/Source/CartManager.cpp b/Source/CartManager.cpp index 6c09a692..f80cf2e6 100644 --- a/Source/CartManager.cpp +++ b/Source/CartManager.cpp @@ -219,14 +219,14 @@ void CartManager::updateCartFilename() { void CartManager::programSelected(ProgramListBox *source, int pos) { if ( source == activeCart.get() ) { - browserCart->setSelected(-1); + browserCart->setActive(-1); mainWindow->processor->setCurrentProgram(pos); mainWindow->processor->updateHostDisplay(); } else { uint8_t unpackPgm[161]; source->getCurrentCart().unpackProgram(unpackPgm, pos); - activeCart->setSelected(-1); - browserCart->setSelected(pos); + activeCart->setActive(-1); + browserCart->setActive(pos); repaint(); mainWindow->processor->updateProgramFromSysex((uint8_t *) unpackPgm); mainWindow->processor->updateHostDisplay(); @@ -316,8 +316,8 @@ void CartManager::fileClicked(const File& file, const MouseEvent& e) { void CartManager::setActiveProgram(int idx, String activeName) { if ( activeCart->programNames[idx] == activeName ) { - activeCart->setSelected(idx); - browserCart->setSelected(-1); + activeCart->setActive(idx); + browserCart->setActive(-1); } activeCart->repaint(); } @@ -347,7 +347,7 @@ void CartManager::selectionChanged() { } else { browserCart->readOnly = false; } - browserCart->setSelected(-1); + browserCart->setActive(-1); browserCart->setCartridge(browserSysex); } diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index d60af376..6209a489 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -453,6 +453,7 @@ GlobalEditor::GlobalEditor () lfoType->addItem("SINE", 5); lfoType->addItem("S&HOLD", 6); lfoType->setImage(lookAndFeel->imageLFO); + lfoType->setTitle("LFO Waveform"); programs = programSelector.get(); @@ -460,6 +461,7 @@ GlobalEditor::GlobalEditor () imageLight = lookAndFeel->imageLight; setTitle("Global Parameters"); setFocusContainerType(FocusContainerType::focusContainer); + setWantsKeyboardFocus(true); aboutButton->setTitle("About DEXED"); //[/Constructor] } diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index f549d688..c1d8610d 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -297,7 +297,7 @@ OperatorEditor::OperatorEditor () light = lookAndFeel->imageLight; Image tmp = lookAndFeel->imageScaling; - + kbdLeftCurve->addItem("-LN", 1); kbdLeftCurve->addItem("-EX", 2); kbdLeftCurve->addItem("+EX", 3); @@ -313,12 +313,12 @@ OperatorEditor::OperatorEditor () kbdRightCurve->setImage(tmp, posRight); background = lookAndFeel->imageOperator; - opSwitch->setTitle("Operator switch"); opMode->setTitle("Operator Mode"); kbdLeftCurve->setTitle("Keyboard Left Curve"); kbdRightCurve->setTitle("Keyboard Right Curve"); + setWantsKeyboardFocus(true); //[/Constructor] } @@ -629,6 +629,10 @@ void OperatorEditor::mouseDown(const MouseEvent &event) { } } +void OperatorEditor::toggleOpSwitch() { + opSwitch->setToggleState(!opSwitch->getToggleState(), dontSendNotification); +} + //[/MiscUserCode] diff --git a/Source/OperatorEditor.h b/Source/OperatorEditor.h index 630e94e2..b825ca94 100644 --- a/Source/OperatorEditor.h +++ b/Source/OperatorEditor.h @@ -54,6 +54,7 @@ class OperatorEditor : public Component, void updateDisplay(); void updateEnvPos(char pos); void mouseDown(const MouseEvent& e) override; + void toggleOpSwitch(); //[/UserMethods] void paint (juce::Graphics& g) override; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 99d4b0e7..a3e6cdbf 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -37,7 +37,7 @@ DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* owner cartManager(this) { setSize(WINDOW_SIZE_X, (ownerFilter->showKeyboard ? WINDOW_SIZE_Y : WINDOW_SIZE_Y - 94)); - + setExplicitFocusOrder(1); processor = ownerFilter; lookAndFeel->setDefaultLookAndFeel(lookAndFeel); @@ -89,6 +89,7 @@ DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* owner cartManagerCover.addChildComponent(&cartManager); cartManager.setVisible(true); + addKeyListener(this); updateUI(); startTimer(100); } @@ -516,3 +517,34 @@ void DexedAudioProcessorEditor::filesDropped (const StringArray &files, int x, i "Related to file \'"+fn.toStdString()+"\', an unknown exception occured."); }; } + +bool DexedAudioProcessorEditor::keyPressed(const KeyPress& key, Component* originatingComponent) { + int keycode = key.getKeyCode(); + ModifierKeys mods = key.getModifiers(); + + TRACE("key pressed: %d\n", keycode); + + if ( (keycode >= '1' && keycode <= '6') && mods.isCtrlDown() ) { + int op = keycode - '1'; + + if ( mods.isShiftDown() ) { + operators[op].toggleOpSwitch(); + return true; + } + + operators[op].grabKeyboardFocus(); + return true; + } + + if ( keycode == 'G' && mods.isCtrlDown() ) { + global.grabKeyboardFocus(); + return true; + } + + if ( keycode == 'L' && mods.isCtrlDown() ) { + cartShow(); + return true; + } + + return false; +} \ No newline at end of file diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index e9ec245e..4fd1726b 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -32,7 +32,7 @@ /** */ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox::Listener, public Timer, - public FileDragAndDropTarget { + public FileDragAndDropTarget, public KeyListener { MidiKeyboardComponent midiKeyboard; OperatorEditor operators[6]; Colour background; @@ -67,6 +67,8 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox: virtual void filesDropped (const StringArray &files, int x, int y ) override; std::unique_ptr createFocusTraverser() override; + bool keyPressed(const KeyPress& key, Component* originatingComponent) override; + static const int WINDOW_SIZE_X = 866; static const int WINDOW_SIZE_Y = 674; }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 08a9c8b1..be261e55 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -814,6 +814,7 @@ AudioProcessorEditor* DexedAudioProcessor::createEditor() { setDpiScaleFactor(1.0); else setDpiScaleFactor(scaleFactor); + return editor; } diff --git a/Source/ProgramListBox.cpp b/Source/ProgramListBox.cpp index 4f10acb7..20e43ad0 100644 --- a/Source/ProgramListBox.cpp +++ b/Source/ProgramListBox.cpp @@ -23,22 +23,245 @@ #include "DXLookNFeel.h" #include "Dexed.h" +class ProgramLabel : public Component, public DragAndDropTarget { + ProgramListBox *pgmListBox; + bool inDrag = false; + +public: + int idx; + + ProgramLabel(ProgramListBox *pgmListBox, int idx) { + this->pgmListBox = pgmListBox; + this->idx = idx; + setWantsKeyboardFocus(true); + setExplicitFocusOrder(idx+1); + } + + void paint(Graphics &g) override { + if ( inDrag ) { + g.setColour(Colours::black); + g.fillRect(0,0,getWidth(), getHeight()); + return; + } + + if ( pgmListBox->hasContent == false ) + return; + if ( getCurrentlyFocusedComponent() == this ) + g.fillAll(DXLookNFeel::fillColour); + else { + if ( idx % 2 == 0 ) { + auto alternateColour = DXLookNFeel::lightBackground.interpolatedWith (getLookAndFeel().findColour(ListBox::textColourId), 0.03f); + g.fillAll(alternateColour); + } else { + g.fillAll(DXLookNFeel::lightBackground); + } + } + + if ( idx == pgmListBox->activePgm ) { + g.setColour(Colours::white); + } else { + g.setColour(Colours::black); + } + + g.drawFittedText(getProgramName(), 0, 0, getWidth(), getHeight(), Justification::centred, true); + } + + void focusGained(FocusChangeType cause) override { + repaint(); + } + + void focusLost(FocusChangeType cause) override { + repaint(); + } + + String getProgramName() { + return pgmListBox->cartContent.getProgramName(idx); + } + + void mouseDown(const MouseEvent &event) override { + if ( ! pgmListBox->hasContent ) + return; + + if ( event.mods.isPopupMenu()) { + pgmListBox->listener->programRightClicked(pgmListBox, idx); + return; + } + if ( event.getNumberOfClicks() == 2 ) + pgmListBox->listener->programSelected(pgmListBox, idx); + } + + void mouseDrag(const MouseEvent &event) override { + if ( ! pgmListBox->hasContent ) + return; + if ( event.getDistanceFromDragStart() < 7 ) + return; + + if (DragAndDropContainer* const dragContainer = DragAndDropContainer::findParentDragContainerFor(this)) { + Image snapshot (Image::ARGB, getWidth(), getHeight(), true); + Graphics g(snapshot); + paint(g); + void *src = pgmListBox->cartContent.getRawVoice() + (idx*128); + var description = var(src, 128); + dragContainer->startDragging(description, this, snapshot, false); + } + } + + bool isInterestedInDragSource(const SourceDetails& dragSourceDetails) override { + if ( pgmListBox->readOnly ) + return false; + if ( ! pgmListBox->hasContent ) + return false; + + Component *comp = dragSourceDetails.sourceComponent.get(); + + if ( comp == this ) + return false; + if ( dynamic_cast(comp) == nullptr ) + return false; + + return true; + } + + void itemDragEnter(const SourceDetails &dragSourceDetails) override { + inDrag = true; + repaint(); + } + + void itemDragMove(const SourceDetails &dragSourceDetails) override { + + } + + void itemDragExit(const SourceDetails &dragSourceDetails) override { + inDrag = false; + repaint(); + } + + void itemDropped(const SourceDetails& dragSourceDetails) override { + inDrag = false; + + Component *comp = dragSourceDetails.sourceComponent.get(); + ProgramLabel *dest = dynamic_cast(comp); + jassert(dest); + + MemoryBlock* block = dragSourceDetails.description.getBinaryData(); + if ( pgmListBox->listener != nullptr ) + pgmListBox->listener->programDragged(pgmListBox, dest->idx, (char *)block->getData()); + + repaint(); + } + + struct ProgramLabelAH : public juce::AccessibilityHandler { + explicit ProgramLabelAH(ProgramLabel *s): program(s), juce::AccessibilityHandler(*s, juce::AccessibilityRole::listItem) { + } + + virtual String getTitle() const override { + return String(program->idx + 1) + " " + program->getProgramName(); + } + + ProgramLabel *program; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProgramLabelAH); + }; + + std::unique_ptr< AccessibilityHandler> createAccessibilityHandler() override { + return std::make_unique(this); + } +}; + ProgramListBox::ProgramListBox(const String name, int numCols) : Component(name) { cols = numCols; rows = 32 / numCols; - selectedPgm = -1; + activePgm = -1; hasContent = false; - dragCandidate = -1; readOnly = false; programNames.clear(); + + for(int i=0;i<32;i++) { + labels[i].reset(new ProgramLabel(this, i)); + addAndMakeVisible(labels[i].get()); + } + setTitle(name); setWantsKeyboardFocus(true); addKeyListener(this); + setFocusContainerType(FocusContainerType::focusContainer); +} + +void ProgramListBox::resized() { + cellWidth = getWidth() / cols; + cellHeight = getHeight() / rows; + + for(int i=0;i<32;i++) { + int targetCols = i / rows; + int targetRow = i % rows; + labels[i].get()->setBounds(targetCols*cellWidth, targetRow*cellHeight, cellWidth, cellHeight); + } +} + +void ProgramListBox::setCartridge(Cartridge &cart) { + cartContent = cart; + cartContent.getProgramNames(programNames); + hasContent = true; + repaint(); +} + +void ProgramListBox::addListener(ProgramListBoxListener *listener) { + this->listener = listener; +} + +void ProgramListBox::setActive(int idx) { + activePgm = idx; + repaint(); +} + +Cartridge &ProgramListBox::getCurrentCart() { + return cartContent; } +bool ProgramListBox::keyPressed(const KeyPress &key, Component *originatingComponent) { + ProgramLabel *programLabel = dynamic_cast(getCurrentlyFocusedComponent()); + if ( programLabel == nullptr ) + return false; + + if ( key.isKeyCode(KeyPress::returnKey) ) { + activePgm = programLabel->idx; + if ( activePgm != -1 ) { + listener->programSelected(this, activePgm); + } + return true; + } + + int currentIdx = programLabel->idx; + + if ( key.isKeyCode(KeyPress::upKey) ) { + currentIdx--; + if ( currentIdx < 0 ) + currentIdx += rows; + } else if ( key.isKeyCode(KeyPress::downKey) ) { + currentIdx++; + if ( currentIdx >= 32 ) + currentIdx -= rows; + } else if ( key.isKeyCode(KeyPress::leftKey) ) { + currentIdx -= rows; + if ( currentIdx < 0 ) + currentIdx += 32; + } else if ( key.isKeyCode(KeyPress::rightKey) ) { + currentIdx += rows; + if ( currentIdx >= 32 ) + currentIdx -= 32; + } else { + return false; + } + + labels[currentIdx]->grabKeyboardFocus(); + + repaint(); + return true; +} + +/* + void ProgramListBox::paint(Graphics &g) { int pgm = 0; - g.setColour(Colour(20,18,18)); g.fillRect(0,0,getWidth(), getHeight()); g.setColour(Colour(0,0,0)); @@ -61,10 +284,11 @@ void ProgramListBox::paint(Graphics &g) { Line line(2, cellHeight*i,getWidth(),cellHeight*i); g.drawDashedLine(line, dashLength, 2); } - + */ + /* for(int i=0;ilistener = listener; -} - -int ProgramListBox::programPosition(int x, int y) { +*/ +/*int ProgramListBox::programPosition(int x, int y) { return (y / cellHeight) + ((x / cellWidth) * rows); -} - -void ProgramListBox::mouseDown(const MouseEvent &event) { - if ( ! hasContent ) - return; - - int pos = programPosition(event.getMouseDownX(), event.getMouseDownY()); - - if ( event.mods.isPopupMenu()) { - listener->programRightClicked(this, pos); - return; - } - - listener->programSelected(this, pos); -} - -void ProgramListBox::mouseUp(const MouseEvent &event) { -} - -void ProgramListBox::mouseDrag(const MouseEvent &event) { - if ( ! hasContent ) - return; - if ( dragCandidate != -1 ) - return; - if ( event.getDistanceFromDragStart() < 7 ) - return; - - if (DragAndDropContainer* const dragContainer = DragAndDropContainer::findParentDragContainerFor(this)) { - Image snapshot (Image::ARGB, cellWidth, cellHeight, true); - int position = programPosition(event.getMouseDownX(), event.getMouseDownY()); - Graphics g(snapshot); - g.setColour(DXLookNFeel::lightBackground); - g.fillRect(0,0,cellWidth, cellHeight); - g.setColour(Colours::white); - g.drawFittedText(programNames[position], 0, 0, cellWidth, cellHeight, Justification::centred, true); - void *src = cartContent.getRawVoice() + (position*128); - var description = var(src, 128); - dragContainer->startDragging(description, this, snapshot, false); - } -} - -void ProgramListBox::setSelected(int idx) { - selectedPgm = idx; -} - -Cartridge &ProgramListBox::getCurrentCart() { - return cartContent; -} +}*/ +/* bool ProgramListBox::isInterestedInDragSource (const SourceDetails& dragSourceDetail) { if ( readOnly ) return false; @@ -164,8 +326,8 @@ bool ProgramListBox::isInterestedInDragSource (const SourceDetails& dragSourceDe return false; return true; -} - +}*/ +/* void ProgramListBox::itemDropped(const SourceDetails& dragSourceDetails) { dragCandidate = programPosition(dragSourceDetails.localPosition.x, dragSourceDetails.localPosition.y); @@ -190,29 +352,4 @@ void ProgramListBox::itemDragExit(const SourceDetails &dragSourceDetails) { dragCandidate = -1; repaint(); } - -bool ProgramListBox::keyPressed(const KeyPress &key, Component *originatingComponent) { - - if ( key.getKeyCode() == KeyPress::upKey ) { - selectedPgm--; - if ( selectedPgm < 0 ) - selectedPgm += rows; - } else if ( key.getKeyCode() == KeyPress::downKey ) { - selectedPgm++; - if ( selectedPgm >= 32 ) - selectedPgm -= rows; - } else if ( key.getKeyCode() == KeyPress::leftKey ) { - selectedPgm -= rows; - if ( selectedPgm < 0 ) - selectedPgm += 32; - } else if ( key.getKeyCode() == KeyPress::rightKey ) { - selectedPgm += rows; - if ( selectedPgm >= 32 ) - selectedPgm -= 32; - } else { - return false; - } - - repaint(); - return true; -} +*/ diff --git a/Source/ProgramListBox.h b/Source/ProgramListBox.h index f7bfe461..0bd92977 100644 --- a/Source/ProgramListBox.h +++ b/Source/ProgramListBox.h @@ -27,50 +27,35 @@ class ProgramListBox; class ProgramListBoxListener { public: - virtual ~ProgramListBoxListener() {} + virtual ~ProgramListBoxListener() = default; virtual void programSelected(ProgramListBox *source, int pos) = 0; virtual void programRightClicked(ProgramListBox *source, int pos) = 0; virtual void programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) = 0; }; -class ProgramListBox : public Component, public DragAndDropTarget, public KeyListener { - ProgramListBoxListener *listener; +class ProgramListBox : public Component, public KeyListener { + ProgramListBoxListener *listener; + Cartridge cartContent; + std::unique_ptr labels[32]; + bool hasContent; - bool showPgmNumber; int cols, rows; int cellWidth, cellHeight; - int programPosition(int x, int y); - int selectedPgm; + int activePgm; - Cartridge cartContent; - - int dragCandidate; + friend class ProgramLabel; public: - StringArray programNames; - + StringArray programNames; bool readOnly; ProgramListBox(const String name, int numCols); void addListener(ProgramListBoxListener *listener); - void paint(Graphics &g) override; void resized() override; - void mouseDown(const MouseEvent &event) override; - void mouseDrag(const MouseEvent &event) override; - void mouseUp(const MouseEvent &event) override; - void setSelected(int idx); + void setActive(int idx); Cartridge &getCurrentCart(); void setCartridge(Cartridge &cart); - - bool isInterestedInDragSource(const SourceDetails& dragSourceDetails) override; - void itemDragEnter(const SourceDetails &dragSourceDetails) override; - void itemDragMove(const SourceDetails &dragSourceDetails) override; - void itemDragExit(const SourceDetails &dragSourceDetails) override; - void itemDropped(const SourceDetails& dragSourceDetails) override; - bool keyPressed (const KeyPress& key, Component* originatingComponent) override; - - std::unique_ptr< AccessibilityHandler > createAccessibilityHandler(ProgramListBox *programListBox); };