From bfe03cbbdb35e09c4fe2a3ef314435c04cbd2522 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Thu, 1 Aug 2024 23:19:37 -0400 Subject: [PATCH 01/17] Keybinding WIP --- Source/CartManager.cpp | 62 ++++++++++++++++++++++++++++++++++++-- Source/CartManager.h | 6 +++- Source/PluginEditor.h | 1 + Source/PluginProcessor.cpp | 1 - Source/ProgramListBox.cpp | 29 +++++++++++++++++- Source/ProgramListBox.h | 6 ++-- 6 files changed, 97 insertions(+), 8 deletions(-) diff --git a/Source/CartManager.cpp b/Source/CartManager.cpp index bfd4a5d2..1c32a5a6 100644 --- a/Source/CartManager.cpp +++ b/Source/CartManager.cpp @@ -71,6 +71,52 @@ public : } }; +/** + * This is a hack to override global dexed focus traversal when the cart manager is shown. We could have used a + * modal window but this would have blocked the keys and the user would not have been able to play the current + * selected preset. Anything that is keyboard related should be ordered in the focusOrder vector. + */ +class CartBrowserFocusTraverser : public KeyboardFocusTraverser { + std::vector &orders; + Component *root; +public: + CartBrowserFocusTraverser(Component *root, std::vector &orders) : orders(orders), root(root) {} + + Component* getDefaultComponent(Component* parentComponent) override { + return orders[0]; + } + + Component* getNextComponent(Component* current) override { + for (int i=0;i getAllComponents(Component* parentComponent) override { + return orders; + } +}; + CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartManager") { mainWindow = editor; cartDir = DexedAudioProcessor::dexedCartDir; @@ -116,6 +162,15 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan activeCartName.reset(new CartridgeFileDisplay()); addAndMakeVisible(activeCartName.get()); + focusOrder.push_back(cartBrowser.get()); + focusOrder.push_back(activeCart.get()); + focusOrder.push_back(browserCart.get()); + focusOrder.push_back(closeButton.get()); + focusOrder.push_back(loadButton.get()); + focusOrder.push_back(saveButton.get()); + focusOrder.push_back(fileMgrButton.get()); + focusOrder.push_back(activeCartName.get()); + /* * * I've removed this since it only works on the DX7 II. TBC. @@ -136,6 +191,10 @@ CartManager::~CartManager() { cartBrowserList.reset(NULL); } +std::unique_ptr CartManager::createFocusTraverser() { + return std::make_unique(this, focusOrder); +} + void CartManager::resized() { float activeSize = ((float) getWidth() - 30) / 8; @@ -322,7 +381,6 @@ void CartManager::programRightClicked(ProgramListBox *source, int pos) { mainWindow->processor->sendCurrentSysexCartridge(); break; } - } void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) { @@ -373,5 +431,3 @@ void CartManager::showSysexConfigMsg() { // unused stuff from FileBrowserListener void CartManager::browserRootChanged (const File& newRoot) {} - - diff --git a/Source/CartManager.h b/Source/CartManager.h index 39861c3e..a5019379 100644 --- a/Source/CartManager.h +++ b/Source/CartManager.h @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2015 Pascal Gauthier. + * Copyright (c) 2015-2024 Pascal Gauthier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -83,6 +83,8 @@ class CartManager : public Component, public Button::Listener, public DragAndDr void showSysexConfigMsg(); + std::vector focusOrder; + public: CartManager(DexedAudioProcessorEditor *editor); virtual ~CartManager(); @@ -107,6 +109,8 @@ class CartManager : public Component, public Button::Listener, public DragAndDr virtual bool keyPressed(const KeyPress& key, Component* originatingComponent) override; void initialFocus(); + + std::unique_ptr createFocusTraverser() override; }; #endif // CARTMANAGER_H_INCLUDED diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 5a269ec0..b380f9ad 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -41,6 +41,7 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox: Component cartManagerCover; SharedResourcePointer lookAndFeel; + public: DexedAudioProcessor *processor; GlobalEditor global; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index c7ca4ba1..bdd2ac0d 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -306,7 +306,6 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi buffer.copyFrom(1, 0, channelData, numSamples, 1); } - //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() { diff --git a/Source/ProgramListBox.cpp b/Source/ProgramListBox.cpp index cac09e66..635f6b1b 100644 --- a/Source/ProgramListBox.cpp +++ b/Source/ProgramListBox.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2015 Pascal Gauthier. + * Copyright (c) 2015-2024 Pascal Gauthier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,6 +31,8 @@ ProgramListBox::ProgramListBox(const String name, int numCols) : Component(name) dragCandidate = -1; readOnly = false; programNames.clear(); + setWantsKeyboardFocus(true); + addKeyListener(this); } void ProgramListBox::paint(Graphics &g) { @@ -188,3 +190,28 @@ void ProgramListBox::itemDragExit(const SourceDetails &dragSourceDetails) { 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; +} \ No newline at end of file diff --git a/Source/ProgramListBox.h b/Source/ProgramListBox.h index a8ee40a2..4aa831ed 100644 --- a/Source/ProgramListBox.h +++ b/Source/ProgramListBox.h @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2015 Pascal Gauthier. + * Copyright (c) 2015-2024 Pascal Gauthier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ class ProgramListBoxListener { virtual void programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) = 0; }; -class ProgramListBox : public Component, public DragAndDropTarget { +class ProgramListBox : public Component, public DragAndDropTarget, public KeyListener { ProgramListBoxListener *listener; bool hasContent; bool showPgmNumber; @@ -67,6 +67,8 @@ class ProgramListBox : public Component, public DragAndDropTarget { 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; }; From e2575fbeec9120662644077c2974c459a8f04a88 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sat, 3 Aug 2024 11:58:38 -0400 Subject: [PATCH 02/17] Upgrade LookAndFeel_V4 --- Source/DXLookNFeel.cpp | 18 +++++------------- Source/DXLookNFeel.h | 12 +++++------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Source/DXLookNFeel.cpp b/Source/DXLookNFeel.cpp index 85d70bd2..c5a1db87 100644 --- a/Source/DXLookNFeel.cpp +++ b/Source/DXLookNFeel.cpp @@ -217,24 +217,16 @@ void DXLookNFeel::drawButtonBackground(Graphics &g, Button &button, const Colour g.drawImage(imageButton, w-3, 0, 3, 30, 47, isButtonDown ? 30 : 0, 47, 30); } -void DXLookNFeel::drawLinearSliderBackground (Graphics&, int x, int y, int width, int height, - float sliderPos, float minSliderPos, float maxSliderPos, - const Slider::SliderStyle st, Slider& s) { - // NOP -} - -void DXLookNFeel::drawLinearSliderThumb (Graphics& g, int x, int y, int width, int height, - float sliderPos, float minSliderPos, float maxSliderPos, - const Slider::SliderStyle st, Slider& s) { - // TODO: find out why the V4 LookNFeel doesn't call this - // TRACE("draw slider"); +void DXLookNFeel::drawLinearSlider (Graphics& g, int x, int y, int width, int height, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle style, Slider& slider) { if ( imageSlider.isNull() ) { - LookAndFeel_V3::drawLinearSliderThumb(g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, st, s); + LookAndFeel_V3::drawLinearSliderThumb(g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); return; } int p = sliderPos - minSliderPos; - p -= 6; + p -= 2; g.drawImage(imageSlider, p, 0, 26, 26, 0, 0, 52, 52); } diff --git a/Source/DXLookNFeel.h b/Source/DXLookNFeel.h index dfb8cbe7..f3d58dec 100644 --- a/Source/DXLookNFeel.h +++ b/Source/DXLookNFeel.h @@ -29,7 +29,7 @@ class LightedToggleButton : public ToggleButton { }; -class DXLookNFeel : public LookAndFeel_V3 { +class DXLookNFeel : public LookAndFeel_V4 { HashMap colourMap; public: @@ -43,14 +43,12 @@ class DXLookNFeel : public LookAndFeel_V3 { virtual void drawRotarySlider(Graphics &g, int x, int y, int width, int height, float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, Slider &slider ) override; virtual void drawToggleButton(Graphics& g, ToggleButton& button, bool isMouseOverButton, bool isButtonDown) override; - virtual void drawLinearSliderBackground (Graphics&, int x, int y, int width, int height, - float sliderPos, float minSliderPos, float maxSliderPos, - const Slider::SliderStyle, Slider&) override; - virtual void drawLinearSliderThumb (Graphics&, int x, int y, int width, int height, - float sliderPos, float minSliderPos, float maxSliderPos, - const Slider::SliderStyle, Slider&) override; virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour, bool isMouseOverButton, bool isButtonDown) override; + virtual void drawLinearSlider (Graphics&, int x, int y, int width, int height, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle, Slider&) override; + //virtual Font getTextButtonFont(TextButton&, int buttonHeight) override; virtual Typeface::Ptr getTypefaceForFont(const Font &) override; virtual void positionComboBoxText (ComboBox& box, Label& label) override; From e88ed43cf6d77dc9f987c2fc62bb6ee623fe57c3 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sat, 3 Aug 2024 14:07:33 -0400 Subject: [PATCH 03/17] Fix Windows installer (virus warning) --- Resources/Installers/Windows/dexed.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Installers/Windows/dexed.iss b/Resources/Installers/Windows/dexed.iss index f623b755..3f7c2e53 100644 --- a/Resources/Installers/Windows/dexed.iss +++ b/Resources/Installers/Windows/dexed.iss @@ -3,7 +3,7 @@ AppName=Dexed AppVersion=0.9.7 DefaultDirName={commonpf64}\Dexed DefaultGroupName=Dexed -Compression=lzma2 +Compression=zip SolidCompression=yes OutputDir=.\ OutputBaseFilename=DexedInstaller @@ -23,7 +23,7 @@ Name: "clap"; Description: "64-bit CLAP Plugin"; Types: full custom; [Files] Source: "Dexed.exe"; DestDir: "{app}"; Components:app; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "Dexed.vst3"; DestDir: "{commoncf64}\VST3"; Components:vst3_64; Flags: ignoreversion +Source: "Dexed.vst3"; DestDir: "{commoncf64}\VST3"; Components:vst3_64; Flags: ignoreversion Source: "Dexed.clap"; DestDir: "{commoncf64}\CLAP"; Components:clap; Flags: ignoreversion [Icons] From 453b16cf558ad28a20f6d8db37939418f5d6a217 Mon Sep 17 00:00:00 2001 From: FulopNandor Date: Sat, 27 Jul 2024 14:17:30 +0200 Subject: [PATCH 04/17] code cleanup for MidiMonitor --- Source/GlobalEditor.cpp | 38 +++++++++++++++++++++++++++----------- Source/GlobalEditor.h | 9 ++++++++- Source/PluginProcessor.cpp | 4 +++- Source/SysexComm.cpp | 15 +++++++++++++-- Source/SysexComm.h | 12 +++++++++++- 5 files changed, 62 insertions(+), 16 deletions(-) diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index 12978201..d0ac83a2 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -31,14 +31,25 @@ * Ugly but useful midi monitor to know if you are really sending/receiving something from the DX7 * If the midi is not configured this component wont show up * + */ +#ifdef IMPLEMENT_MidiMonitor class MidiMonitor : public Component { SysexComm *midi; Image light; + int imageHeight; + int imageHeight2; + int imageWidth; + SharedResourcePointer lookAndFeel; public: MidiMonitor(SysexComm *sysexComm) { midi = sysexComm; - light = DXLookNFeel::getLookAndFeel()->imageLight; + light = lookAndFeel->imageLight; + imageHeight = light.getHeight(); + imageHeight2 = imageHeight / 2; + imageWidth = light.getWidth(); + + TRACE("WARNING! This functionality is a candidate for deprecation/obsolescence!"); } void paint(Graphics &g) { @@ -47,24 +58,25 @@ class MidiMonitor : public Component { g.setColour(Colours::white); if ( midi->isInputActive() ) { - g.drawSingleLineText("DX7 IN", 17,14); - g.drawImage(light, 0, 3, 14, 14, 0, midi->inActivity ? 14 : 0, 14, 14); + g.drawSingleLineText("DX7 IN", 24, 18); + g.drawImage(light, 0, 0, imageWidth, imageHeight2, 0, midi->inActivity ? imageHeight2 : 0, imageWidth, imageHeight2); midi->inActivity = false; } if ( midi->isOutputActive() ) { - g.drawSingleLineText("DX7 OUT", 17, 28); - g.drawImage(light, 0, 17, 14, 14, 0, midi->outActivity ? 14 : 0, 14, 14); + g.drawSingleLineText("DX7 OUT", 24, 36); + g.drawImage(light, 0, 18, imageWidth, imageHeight2, 0, midi->outActivity ? imageHeight2 : 0, imageWidth, imageHeight2); midi->outActivity = false; } } -};*/ +}; +#endif //IMPLEMENT_MidiMonitor class AboutBox : public DialogWindow { public: Image logo_png; std::unique_ptr dexed; // changed to std::unique_ptr from juce::ScopedPointer - std::unique_ptr surge; // changed to std__unique_ptr from juce::ScopedPointer + std::unique_ptr surge; // changed to std::unique_ptr from juce::ScopedPointer AboutBox(Component *parent) : DialogWindow("About", Colour(0xFF000000), true), dexed(std::make_unique("https://asb2m10.github.io/dexed/", URL("https://asb2m10.github.io/dexed/"))), @@ -706,9 +718,11 @@ void GlobalEditor::bind(DexedAudioProcessorEditor *edit) { editor = edit; - //midiMonitor = new MidiMonitor(&(processor->sysexComm)); - //addAndMakeVisible(midiMonitor); - //midiMonitor->setBounds(155, 21, 80, 45); +#ifdef IMPLEMENT_MidiMonitor + midiMonitor = std::make_unique(&(processor->sysexComm)); + addAndMakeVisible(*midiMonitor); + midiMonitor->setBounds(110, 10, 80, 45); //midiMonitor->setBounds(155, 21, 80, 45); +#endif //IMPLEMENT_MidiMonitor repaint(); } @@ -734,7 +748,9 @@ void GlobalEditor::updatePitchPos(int pos) { void GlobalEditor::updateVu(float f) { vuOutput->v = f; vuOutput->repaint(); - //midiMonitor->repaint(); +#ifdef IMPLEMENT_MidiMonitor + midiMonitor->repaint(); +#endif //IMPLEMENT_MidiMonitor } void GlobalEditor::setMonoState(bool state) { diff --git a/Source/GlobalEditor.h b/Source/GlobalEditor.h index e892a1d6..65b59b43 100644 --- a/Source/GlobalEditor.h +++ b/Source/GlobalEditor.h @@ -25,6 +25,10 @@ #include "DXComponents.h" #include "AlgoDisplay.h" +#ifdef IMPLEMENT_MidiMonitor +#include "SysexComm.h" +#endif // IMPLEMENT_MidiMonitor + class DexedAudioProcessorEditor; //[/Headers] @@ -59,7 +63,10 @@ class GlobalEditor : public Component, void setMonoState(bool state); ProgramSelector *programs; - //std::unique_ptr midiMonitor; + +#ifdef IMPLEMENT_MidiMonitor + std::unique_ptr midiMonitor; +#endif //IMPLEMENT_MidiMonitor void mouseDown(const MouseEvent& e) override; //[/UserMethods] diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index bdd2ac0d..d4ab38b8 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -599,7 +599,9 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid if ( message.isActiveSense() ) return; - sysexComm.inActivity = true; +#ifdef IMPLEMENT_MidiMonitor + sysexComm.inActivity = true; // indicate to MidiMonitor that a MIDI messages (other than Active Sense) is received +#endif //IMPLEMENT_MidiMonitor const uint8 *buf = message.getRawData(); int sz = message.getRawDataSize(); diff --git a/Source/SysexComm.cpp b/Source/SysexComm.cpp index c4e70aee..8941bf0d 100644 --- a/Source/SysexComm.cpp +++ b/Source/SysexComm.cpp @@ -31,6 +31,11 @@ SysexComm::SysexComm() { input = NULL; output = NULL; inputOutput = false; + +#ifdef IMPLEMENT_MidiMonitor + inActivity = false; + outActivity = false; +#endif //IMPLEMENT_MidiMonitor } String SysexComm::getInput() { @@ -38,9 +43,11 @@ String SysexComm::getInput() { } bool SysexComm::setInput(String target) { +#ifndef IMPLEMENT_MidiMonitor if ( JUCEApplication::isStandaloneApp() ) return true; - +#endif + if ( input != NULL ) { input->stop(); input = NULL; @@ -132,7 +139,11 @@ void SysexComm::setChl(int chl) { int SysexComm::send(const MidiMessage &message) { if ( output == NULL ) return 2; - outActivity = true; + +#ifdef IMPLEMENT_MidiMonitor + outActivity = true; // indicate to MidiMonitor that a MIDI message is going to be sent +#endif // IMPLEMENT_MidiMonitor + output->sendMessageNow(message); return 0; } diff --git a/Source/SysexComm.h b/Source/SysexComm.h index 871266b3..2db16e3f 100644 --- a/Source/SysexComm.h +++ b/Source/SysexComm.h @@ -23,6 +23,15 @@ #include "../JuceLibraryCode/JuceHeader.h" + // If the macro ''IMPLEMENT_MidiMonitor'' is defined, + // then the class ''MidiMonitor'' and its related variables + // and invocations from other parts are implemented. + // If the macro is not defined the related source snippets are excluded. + // WARNING: this class and related variables and invocations + // are very likely candidates for deprecation / obsolescence, + // so it is NOT RECOMMENDED to define this macro! + //#define IMPLEMENT_MidiMonitor + class SysexComm { std::unique_ptr input; std::unique_ptr output; @@ -35,9 +44,10 @@ class SysexComm { MidiBuffer noteOutput; public : MidiInputCallback *listener; +#ifdef IMPLEMENT_MidiMonitor bool inActivity; bool outActivity; - +#endif //IMPLEMENT_MidiMonitor SysexComm(); bool setInput(String name); From 3d26fcaa129659180178ccf39b6bd458fc60ce1b Mon Sep 17 00:00:00 2001 From: FulopNandor Date: Sun, 28 Jul 2024 21:34:37 +0200 Subject: [PATCH 05/17] corrections for DexedTheme v2 --- Documentation/DexedTheme.md | 44 ++++- Documentation/GreenLight_28x28.png | Bin 0 -> 2238 bytes Source/DXLookNFeel.cpp | 252 +++++++++++++++++++++-------- 3 files changed, 219 insertions(+), 77 deletions(-) create mode 100644 Documentation/GreenLight_28x28.png diff --git a/Documentation/DexedTheme.md b/Documentation/DexedTheme.md index 427dd102..5dcf714c 100644 --- a/Documentation/DexedTheme.md +++ b/Documentation/DexedTheme.md @@ -1,11 +1,14 @@ Dexed Theme =========== +Last update: 26 July 2024 + Dexed UI can be themed if you want a different colour combination or background images. -You need need to create a file named "DexedTheme.xml" and it must be placed in the same directory where "Dexed.xml" is created. On Windows, this is C:\Users\\\AppData\Roaming\DigitalSuburban" and on Mac it is "~/Library/Application Support/DigitalSuburban" +You need need to create a file named "DexedTheme.xml" and it must be placed in the same directory where "Dexed.xml" is created. On Windows, this is "C:\Users\<your user name>\AppData\Roaming\DigitalSuburban\Dexed" and on Mac it is "~/Library/Application Support/DigitalSuburban/Dexed" (FIXME!), and in Linux it is "~/.local/share/DigitalSuburban/Dexed". Colour id are analogous to JUCE color ID; see [DXLookNFeel() class](../Source/DXLookNFeel.cpp) for a complete list a defined colours id. Colour value is the hexa decimal values (from 0 to 0xFF) for ALPHA RED BLUE GREEN. +For ALPHA, 0x00 is completely transparent, 0xFF is opaque. Known colour keys; simply override those you want different. @@ -28,18 +31,47 @@ PopupMenu::backgroundColourId PopupMenu::textColourId PopupMenu::highlightedTextColourId PopupMenu::highlightedBackgroundColourId +TreeView::backgroundColourId +DirectoryContentsDisplayComponent::highlightColourId +DirectoryContentsDisplayComponent::textColourId +ScrollBar::thumbColourId Dexed::backgroundId Dexed::fillColourId ``` Image id are the file name defined in [ui folder](../Resources/ui). If it cannot find the file, the image will no longer be rendered. The image path is relative to the path where "DexedTheme.xml" is defined. +Note that sizes in pixels and structures of the images should be same as original ones, because the layout of GUI based on these hardcoded fix sizes. Recently, the following image ids could be specified: + +Knob_68x68.png +Switch_96x52.png +SwitchLighted_48x26.png +Switch_64x64.png +ButtonUnlabeled_50x30.png +Slider_52x52.png +Scaling_36_26.png +Light_28x28.png +LFO_36_26.png +OperatorEditor_574x436.png +GlobalEditor_1728x288.png + -Example configuration ---------------------- +Example ``DexedTheme.xml`` configuration +---------------------------------------- ```xml - - + + + + + + -``` \ No newline at end of file +``` + +As the result: +- the colors of the background of popup menus, the text of normal menu items, and the text and background of highlighted menu items are changed to opaque blue, white, yellow, and cyan, +- the color of thumbnail of scrollbars is changed to opaque lightgray, +- the color of bright LEDs are changed to green from red (if there is a folder named ``myTheme`` within the subdirectory where your ``Dexed.xml`` and ``DexedTheme.xml`` files are located, and there is the file ``GreenLight_28x28.png`` containing the image of a red LED inside that ``myTheme`` subdirectory). + +Note that ``DexedTheme.xml``are loaded only once during the initialization when Dexed (either the Standalone or the plugin version) is launched. \ No newline at end of file diff --git a/Documentation/GreenLight_28x28.png b/Documentation/GreenLight_28x28.png new file mode 100644 index 0000000000000000000000000000000000000000..07d305b6b90ad229a14218e163a9d4cf9b7ef30b GIT binary patch literal 2238 zcmV;v2toIWP)`}Bhke%?`SH$~DSNupPcPqvRyC zD_4+D{_?#a{o%tef4jWgz0hdph_(32F&vh6Rrwi7|KpQB`3qoI%!94#Ki%Be`Sox# zZu&algAzkT)nKfp*~+T(>x;j<^oIN0)vF)-|4CuHG5B%0c;+`*l4aI_BnB}cC?R-U zRkPbK^HJaZ>c-FvuUz57t5;D#Pp$C%_pdaqExwE@7pqYp2{j6c5s*lXk*ccM-Z^A* zYoFRt?#~bvC~>$ueZha=GRO{gW1E25`3Vp9cf}&T3SS` zy;zm?W#Er)huwZ}pRz2mSw^edB`*rBF&Hs(r3Py(X`0d~nndOMmoAY% zOU!c>-ul;<$v3y|aQ2n6EH8H{8VPA?5Mw}bNKWKPAeFZo;wZfh4gM*@zZ*@DZ zH5Ve|X^pWHlG-97!~otwh~Rx-XJ>D`x%pt?99^FAy6kg!GO0tuS~=frHZCXDl30r( z#1N^gKwU+giRTC5jlIn5W$@eI8)W4;yi{Ggf@W&LO~!y$N)ug zC?%E>Wr}4=WS^W3V1zuGDo<7#ML1-IoOP51919p0i8K+~2rVM%{BDmjA}~TZL>Qpt z)HJENkFxtXGaIighzN#NJX4@1 zM4z zCl}60H|lhl>dBp@Ns;_uT)cWe8}@2Am=}o_h614h-vW=8G{KS) zC=e0?_6^n+-f@?kX`!riy9_2b@o|F?rid6)1VRoml6%K@v{fvPlGO)Ie+MC*!?HQd z0&0!{4VDBUA!1N$Bh`FVj}2~?d_|UGMMr5)Z_##0ZZh# zC^<@t$9Fn{LSG-qpZxE+G+Lp-Aq`OqB35Qwi>N|CP#Y0Z#4EnE8P+vvV~+KZhb1&& zHXh81aUdY`1LAI7y6f>~taZVjjTtLau&6nO7*v#6BF+SSR2+t(D|PHVWLP{(MvcKU zoBkBgj9t4PYjVmER%WlZz4?9FzlIf_nPaqYh7wzObV+u;;6}tAOu2) z?8d;K``g+-jAbYoqKtq;gb5i_G6wUS8WV(wq7)2p3~@9NGK3Cr5Q%$tv}_GWJ94gS zv@W#T_VvWjp&-V{?Q+T|dpogL`4Xj1q({mw83zbM%zS2@PXo!rm{dkYb}AnxMgO2K;AySgDkCAN}xbS`!_r_%K^+VRofqf>99wks4l8_3&OH+LFzk`Bb$sM_v>ytG z9I}BR8$Wdr%P7d{CDXwzfF zY&5cXa%wDNRvA#R$AAUKGzeIU5>%Mb)qPgkqsuNSdl>df*#>%#yMOl7rgW(|hhdq( z0+AL%3n9mm5)mK}DM=X9;1K1Ilzm_mVfZw+FB%h5pbK=V>5!50=&t#wij>R(<0u_Y zIVL^{w(v;RKiWOTInd3I@U+qJxSi|n +#include + #include "DXLookNFeel.h" #include "DXComponents.h" #include "Dexed.h" @@ -25,14 +28,61 @@ #define REG_COLOUR(id, value) setColour(id, value); colourMap.set(#id, id) +/* +// Loads an image from the file specified by ``path`` and +// returns it. +// Otherwise it returns ``nullptr``, if the image was not loaded successfully. +// +// I propose it for deprecation. +// Image findImage(String path) { Image img; if ( path.length() <= 3 ) return img; - File imgFile(path); + //File imgFile(path); // it might cause an assertion in juce_File.cpp + File imgFile = File::getCurrentWorkingDirectory().getChildFile(path); img = ImageCache::getFromFile(imgFile); + if (img.isNull()) { + TRACE("img.isNull() == true: cannot load image from the path=%s", path.toRawUTF8()); + } return img; } +*/ + + +// Loads an image from the file specified by ``path`` and +// strore it in variable ``image``, only if the image was +// loaded successfully from the file. +// Otherwise, content of variable ``image`` is not altered. +void replaceImage(String path, Image &image) { + Image img; + if (path.length() <= 3) { + return; + } + File imgFile = File::getCurrentWorkingDirectory().getChildFile(path); + img = ImageCache::getFromFile(imgFile); + if (!img.isNull()) { + // check sizes + int hnew = img.getWidth(); + int wnew = img.getHeight(); + int horig = image.getHeight(); + int worig = image.getWidth(); + if (horig != hnew || worig != wnew) { + TRACE("WARNING: new image sizes (h=%d,w=%d) differ from original ones (h=%d, w=%d)", hnew, wnew, horig, worig); + } + + // store the new image + // + // TODO: if we really do not want allow to replace the original image + // with a new one having different size, then the previous ``if`` statement + // should be supplemented with an ``else`` branch, and the following + // assignment should be moved into this ``else`` branch. + image = img; + } + else { + TRACE("ERROR: img.isNull() == true: cannot load image from the path=%s", path.toRawUTF8()); + } +} DXLookNFeel::DXLookNFeel() { Colour ctrlBackground; @@ -40,6 +90,9 @@ DXLookNFeel::DXLookNFeel() { DexedAudioProcessor::dexedAppDir.setAsCurrentWorkingDirectory(); ctrlBackground = Colour(20,18,18); + // WARNING! If you modify the colour IDs here, please actualize the file ''DexedTheme.md'' + // in the subdirectory ``~/dexed/Documentation/`` + REG_COLOUR(TextButton::buttonColourId,Colour(0xFF0FC00F)); REG_COLOUR(TextButton::textColourOnId, Colours::white); REG_COLOUR(TextButton::textColourOffId, Colours::white); @@ -61,7 +114,12 @@ DXLookNFeel::DXLookNFeel() { REG_COLOUR(TreeView::backgroundColourId, background); REG_COLOUR(DirectoryContentsDisplayComponent::highlightColourId, fillColour); REG_COLOUR(DirectoryContentsDisplayComponent::textColourId, Colours::white); - + + // Register ``Scrollbar::thumbColourId`` to allow its redefinion in ``DexedTheme.xml``. + REG_COLOUR(ScrollBar::thumbColourId, background.darker()); + + // WARNING! If you modify the images here, please actualize the file ''DexedTheme.md'' + // in the subdirectory ``~/dexed/Documentation/`` imageKnob = ImageCache::getFromMemory(BinaryData::Knob_68x68_png, BinaryData::Knob_68x68_pngSize); // 2x imageSwitch = ImageCache::getFromMemory(BinaryData::Switch_96x52_png, BinaryData::Switch_96x52_pngSize); // 2x imageSwitchLighted = ImageCache::getFromMemory(BinaryData::SwitchLighted_48x26_png, BinaryData::SwitchLighted_48x26_pngSize); @@ -74,87 +132,138 @@ DXLookNFeel::DXLookNFeel() { imageOperator = ImageCache::getFromMemory(BinaryData::OperatorEditor_574x436_png, BinaryData::OperatorEditor_574x436_pngSize); // 2x imageGlobal = ImageCache::getFromMemory (BinaryData::GlobalEditor_1728x288_png, BinaryData::GlobalEditor_1728x288_pngSize); // 2x + //--- + // load and parse the file ``DexedTheme.xml`` + //--- + File dexedTheme = DexedAudioProcessor::dexedAppDir.getChildFile("DexedTheme.xml"); - if ( ! dexedTheme.existsAsFile() ) + if ( ! dexedTheme.existsAsFile() ) { + TRACE("file \"%s\" does not exists", dexedTheme.getFullPathName()); return; - + } + std::unique_ptr root = XmlDocument::parse(dexedTheme); if ( root == NULL ) + { + TRACE("ERROR: XmlDocument::parse(): failed"); return; + } + + //--- + // Get custom ARGB color codes specified by the + // ``colour id`` - ``value`` pairs in ``DexedTheme.xml`` file. + //--- - forEachXmlChildElementWithTagName(*root, colour, "colour") { - String name = colour->getStringAttribute("id", ""); - if ( name == "" ) - continue; - String value = colour->getStringAttribute("value", ""); - if ( value == "" ) - continue; - if ( value.length() < 8 ) - continue; - int conv = strtol(value.toRawUTF8(), NULL, 16); - if ( colourMap.contains(name) ) { - setColour(colourMap[name], Colour(conv)); - } else { - if ( name == "Dexed::backgroundId" ) { - background = Colour(conv); + //forEachXmlChildElementWithTagName(*root, colour, "colour") { // deprecated! + for (XmlElement* colour = root->getFirstChildElement(); colour != nullptr; colour = colour->getNextElement()) { + if (colour->hasTagName("colour")) { + String name = colour->getStringAttribute("id", ""); + if ( name == "" ) + continue; + String value = colour->getStringAttribute("value", ""); + if ( value == "" ) + continue; + if ( value.length() < 8 ) { + TRACE("ERROR: illegal value=\"%s\" at color id=\"%s\"; skipped", value.toRawUTF8(), name.toRawUTF8()); continue; } - if ( name == "Dexed::fillColourId" ) { - fillColour = Colour(conv); + //int conv = strtol(value.toRawUTF8(), NULL, 16); // as alpha (MSB) could be above 0x7F, hence ``strtol()`` is inappropiate to convert values exceeding ``0x7FFFFFFF`` + char* endptr = NULL; + uint64_t conv = strtoull(value.toRawUTF8(), &endptr, 16); + //TRACE("color id=\"%s\" value=\"%s\": conv=0x%" PRIx64 "", name.toRawUTF8(), value.toRawUTF8(), conv); + if (endptr != nullptr && *endptr != '\0') { + TRACE("ERROR: illegal char #%d in value=\"%s\" at color id=\"%s\"; skipped", (int)(*endptr), value.toRawUTF8(), name.toRawUTF8()); continue; } - } + if (conv > 0xFFFFFFFFULL) { + TRACE("ERROR: value 0x%" PRIx64 " exceeded the limit at color id=\"%s\"; skipped", conv, name.toRawUTF8()); + continue; + } + if ( colourMap.contains(name) ) { + setColour(colourMap[name], Colour((uint32_t)conv)); + } else { + if ( name == "Dexed::backgroundId" ) { + background = Colour((uint32_t)conv); + continue; + } + else if ( name == "Dexed::fillColourId" ) { + fillColour = Colour((uint32_t)conv); + continue; + } + TRACE("ERROR: color id=\"%s\" not found in colourMap; skipped.", name.toRawUTF8()); + } + } } - // TODO: THIS IS DEAD CODE. NOBODY IS USING THIS. - forEachXmlChildElementWithTagName(*root, image, "image") { - String name = image->getStringAttribute("id", ""); - String path = image->getStringAttribute("path", ""); - if ( name == "Knob_34x34.png" ) { - imageKnob = findImage(path); - continue; - } - if ( name == "Switch_48x26.png" ) { - imageSwitch = findImage(path); - continue; - } - if ( name == "SwitchLighted_48x26.png" ) { - imageSwitchLighted = findImage(path); - continue; - } - if ( name == "Switch_32x64.png" ) { - imageSwitchOperator = findImage(path); - continue; - } - if ( name == "ButtonUnlabeled_50x30.png" ) { - imageButton = findImage(path); - continue; - } - if ( name == "Slider_26x26.png" ) { - imageSlider = findImage(path); - continue; - } - if ( name == "Scaling_36_26.png" ) { - imageScaling = findImage(path); - continue; - } - if ( name == "Light_14x14.png" ) { - imageLight = findImage(path); - continue; - } - if ( name == "LFO_36_26.png" ) { - imageLFO = findImage(path); - continue; - } - if ( name == "OperatorEditor_287x218.png" ) { - imageOperator = findImage(path); - continue; - } - if ( name == "GlobalEditor_864x144.png" ) { - imageGlobal = findImage(path); - continue; - } + //--- + // Get the custom images specified by the + // ``image`` - ``path`` pairs in the "Dexed.xml" file. + //--- + + //forEachXmlChildElementWithTagName(*root, image, "image") { // deprecated! + for (XmlElement* image = root->getFirstChildElement(); image != nullptr; image = image->getNextElement()) { + if (image->hasTagName("image")) { + String name = image->getStringAttribute("id", ""); + String path = image->getStringAttribute("path", ""); + //TRACE("image id=\'%s\' path=\'%s\'", name.toRawUTF8(), path.toRawUTF8()); + if (name == /*"Knob_34x34.png"*/"Knob_68x68.png") { // 2x + //imageKnob = findImage(path); // if the new image was not loaded successfully, ``findImage()`` returns ``nullptr``, so the existing ``imageKnob`` is lost + replaceImage(path, imageKnob); + continue; + } + if (name == /*"Switch_48x26.png"*/ "Switch_96x52.png") { // 2x + //imageSwitch = findImage(path); + replaceImage(path, imageSwitch); + continue; + } + if (name == "SwitchLighted_48x26.png") { + //imageSwitchLighted = findImage(path); + replaceImage(path, imageSwitchLighted); + continue; + } + if (name == /*"Switch_32x64.png"*/ "Switch_64x64.png") { // 2x + //imageSwitchOperator = findImage(path); + replaceImage(path, imageSwitchOperator); + continue; + } + if (name == "ButtonUnlabeled_50x30.png") { + //imageButton = findImage(path); + replaceImage(path, imageButton); + continue; + } + if (name == /*"Slider_26x26.png"*/ "Slider_52x52.png") { // 2x + //imageSlider = findImage(path); + replaceImage(path, imageSlider); + continue; + } + if (name == "Scaling_36_26.png") { + //imageScaling = findImage(path); + replaceImage(path, imageScaling); + continue; + } + if (name == /*"Light_14x14.png"*/ "Light_28x28.png") { // 2x + //imageLight = findImage(path); + replaceImage(path, imageLight); + continue; + } + if (name == "LFO_36_26.png") { + //imageLFO = findImage(path); + replaceImage(path, imageLFO); + continue; + } + if (name == /*"OperatorEditor_287x218.png"*/ "OperatorEditor_574x436_png") { // 2x + //imageOperator = findImage(path); + replaceImage(path, imageOperator); + continue; + } + if (name == /*"GlobalEditor_864x144.png"*/ "GlobalEditor_1728x288_png") { // 2x + //imageGlobal = findImage(path); + replaceImage(path, imageGlobal); + continue; + } + TRACE("ERROR: unknown image id=\"%s\"; skipped", name.toRawUTF8()); + } } } @@ -173,7 +282,8 @@ void DXLookNFeel::drawRotarySlider( Graphics &g, int x, int y, int width, int he const int nFrames = imageKnob.getHeight()/imageKnob.getWidth(); // number of frames for vertical film strip const int frameIdx = (int)ceil(fractRotation * ((double)nFrames-1.0) ); // current index from 0 --> nFrames-1 - const float radius = jmin (width / 2.0f, height / 2.0f) ; + //const float radius = jmin (width / 2.0f, height / 2.0f) ; // float multiplication by 0.5 is faster than division by 2.0 + const float radius = jmin(width * 0.5f, height * 0.5f); const float centreX = x + width * 0.5f; const float centreY = y + height * 0.5f; const float rx = centreX - radius - 1.0f; From 4fe8dff57635647d36a94136940dd05faf0f5495 Mon Sep 17 00:00:00 2001 From: FulopNandor Date: Sun, 28 Jul 2024 23:18:33 +0200 Subject: [PATCH 06/17] Fix TRACE macro usage --- Source/DXLookNFeel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/DXLookNFeel.cpp b/Source/DXLookNFeel.cpp index 9b2e3f5e..41809579 100644 --- a/Source/DXLookNFeel.cpp +++ b/Source/DXLookNFeel.cpp @@ -63,8 +63,8 @@ void replaceImage(String path, Image &image) { img = ImageCache::getFromFile(imgFile); if (!img.isNull()) { // check sizes - int hnew = img.getWidth(); - int wnew = img.getHeight(); + int hnew = img.getHeight(); + int wnew = img.getWidth(); int horig = image.getHeight(); int worig = image.getWidth(); if (horig != hnew || worig != wnew) { @@ -139,7 +139,7 @@ DXLookNFeel::DXLookNFeel() { File dexedTheme = DexedAudioProcessor::dexedAppDir.getChildFile("DexedTheme.xml"); if ( ! dexedTheme.existsAsFile() ) { - TRACE("file \"%s\" does not exists", dexedTheme.getFullPathName()); + TRACE("file \"%s\" does not exists", dexedTheme.getFullPathName().toRawUTF8()); return; } From 28bc0bed137d258cb4db5906175f44a6bd22c90d Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sun, 4 Aug 2024 15:10:03 -0400 Subject: [PATCH 07/17] Force focus order --- Source/GlobalEditor.cpp | 105 ++++++++++++++-------- Source/GlobalEditor.h | 2 +- Source/OperatorEditor.cpp | 79 +++++++++++------ Source/OperatorEditor.h | 2 +- Source/ParamDialog.cpp | 181 ++++++++++++++++++++++---------------- Source/ParamDialog.h | 2 +- Source/PluginParam.cpp | 2 + 7 files changed, 227 insertions(+), 146 deletions(-) diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index d0ac83a2..fc62e594 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -7,7 +7,7 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 6.0.7 + Created with Projucer version: 7.0.9 ------------------------------------------------------------------------------ @@ -40,7 +40,7 @@ class MidiMonitor : public Component { int imageHeight2; int imageWidth; - SharedResourcePointer lookAndFeel; + SharedResourcePointer lookAndFeel; public: MidiMonitor(SysexComm *sysexComm) { midi = sysexComm; @@ -95,7 +95,7 @@ class AboutBox : public DialogWindow { surge->setColour(HyperlinkButton::ColourIds::textColourId, Colour(0xFF4ea097)); surge->setJustificationType(Justification::left); surge->setBounds(18, 458, getWidth() - 36, 30); - + // create a new Component to hold ''dexed'' and ''surge'' as subcomponents // and set this holder Component as the content component of the DialogWindow Component* holder = new Component(); @@ -135,6 +135,7 @@ GlobalEditor::GlobalEditor () lfoSpeed.reset (new juce::Slider ("lfoSpeed")); addAndMakeVisible (lfoSpeed.get()); + lfoSpeed->setExplicitFocusOrder (14); lfoSpeed->setRange (0, 99, 1); lfoSpeed->setSliderStyle (juce::Slider::RotaryVerticalDrag); lfoSpeed->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -144,6 +145,7 @@ GlobalEditor::GlobalEditor () lfoAmDepth.reset (new juce::Slider ("lfoAmDepth")); addAndMakeVisible (lfoAmDepth.get()); + lfoAmDepth->setExplicitFocusOrder (19); lfoAmDepth->setRange (0, 99, 1); lfoAmDepth->setSliderStyle (juce::Slider::RotaryVerticalDrag); lfoAmDepth->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -153,6 +155,7 @@ GlobalEditor::GlobalEditor () lfoPitchDepth.reset (new juce::Slider ("lfoPitchDepth")); addAndMakeVisible (lfoPitchDepth.get()); + lfoPitchDepth->setExplicitFocusOrder (18); lfoPitchDepth->setRange (0, 99, 1); lfoPitchDepth->setSliderStyle (juce::Slider::RotaryVerticalDrag); lfoPitchDepth->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -162,6 +165,7 @@ GlobalEditor::GlobalEditor () lfoDelay.reset (new juce::Slider ("lfoDelay")); addAndMakeVisible (lfoDelay.get()); + lfoDelay->setExplicitFocusOrder (15); lfoDelay->setRange (0, 99, 1); lfoDelay->setSliderStyle (juce::Slider::RotaryVerticalDrag); lfoDelay->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -171,6 +175,7 @@ GlobalEditor::GlobalEditor () cutoff.reset (new juce::Slider ("cutoff")); addAndMakeVisible (cutoff.get()); + cutoff->setExplicitFocusOrder (6); cutoff->setRange (0, 1, 0); cutoff->setSliderStyle (juce::Slider::RotaryVerticalDrag); cutoff->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -180,6 +185,7 @@ GlobalEditor::GlobalEditor () reso.reset (new juce::Slider ("reso")); addAndMakeVisible (reso.get()); + reso->setExplicitFocusOrder (7); reso->setRange (0, 1, 0); reso->setSliderStyle (juce::Slider::RotaryVerticalDrag); reso->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -189,6 +195,7 @@ GlobalEditor::GlobalEditor () pitchRate2.reset (new juce::Slider ("pitchRate2")); addAndMakeVisible (pitchRate2.get()); + pitchRate2->setExplicitFocusOrder (24); pitchRate2->setRange (0, 99, 1); pitchRate2->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchRate2->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -198,6 +205,7 @@ GlobalEditor::GlobalEditor () pitchRate3.reset (new juce::Slider ("pitchRate3")); addAndMakeVisible (pitchRate3.get()); + pitchRate3->setExplicitFocusOrder (26); pitchRate3->setRange (0, 99, 1); pitchRate3->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchRate3->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -207,6 +215,7 @@ GlobalEditor::GlobalEditor () pitchRate4.reset (new juce::Slider ("pitchRate4")); addAndMakeVisible (pitchRate4.get()); + pitchRate4->setExplicitFocusOrder (28); pitchRate4->setRange (0, 99, 1); pitchRate4->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchRate4->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -216,6 +225,7 @@ GlobalEditor::GlobalEditor () pitchRate1.reset (new juce::Slider ("pitchRate1")); addAndMakeVisible (pitchRate1.get()); + pitchRate1->setExplicitFocusOrder (22); pitchRate1->setRange (0, 99, 1); pitchRate1->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchRate1->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -225,6 +235,7 @@ GlobalEditor::GlobalEditor () pitchLevel2.reset (new juce::Slider ("pitchLevel2")); addAndMakeVisible (pitchLevel2.get()); + pitchLevel2->setExplicitFocusOrder (23); pitchLevel2->setRange (0, 99, 1); pitchLevel2->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchLevel2->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -234,6 +245,7 @@ GlobalEditor::GlobalEditor () pitchLevel3.reset (new juce::Slider ("pitchLevel3")); addAndMakeVisible (pitchLevel3.get()); + pitchLevel3->setExplicitFocusOrder (25); pitchLevel3->setRange (0, 99, 1); pitchLevel3->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchLevel3->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -243,6 +255,7 @@ GlobalEditor::GlobalEditor () pitchLevel4.reset (new juce::Slider ("pitchLevel4")); addAndMakeVisible (pitchLevel4.get()); + pitchLevel4->setExplicitFocusOrder (27); pitchLevel4->setRange (0, 99, 1); pitchLevel4->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchLevel4->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -252,6 +265,7 @@ GlobalEditor::GlobalEditor () pitchLevel1.reset (new juce::Slider ("pitchLevel1")); addAndMakeVisible (pitchLevel1.get()); + pitchLevel1->setExplicitFocusOrder (21); pitchLevel1->setRange (0, 99, 1); pitchLevel1->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchLevel1->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -261,6 +275,7 @@ GlobalEditor::GlobalEditor () transpose.reset (new juce::Slider ("transpose")); addAndMakeVisible (transpose.get()); + transpose->setExplicitFocusOrder (9); transpose->setRange (0, 48, 1); transpose->setSliderStyle (juce::Slider::RotaryVerticalDrag); transpose->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -270,6 +285,7 @@ GlobalEditor::GlobalEditor () oscSync.reset (new juce::ToggleButton ("oscSync")); addAndMakeVisible (oscSync.get()); + oscSync->setExplicitFocusOrder (20); oscSync->setButtonText (juce::String()); oscSync->addListener (this); @@ -277,6 +293,7 @@ GlobalEditor::GlobalEditor () pitchModSens.reset (new juce::Slider ("pitchModSens")); addAndMakeVisible (pitchModSens.get()); + pitchModSens->setExplicitFocusOrder (17); pitchModSens->setRange (0, 7, 1); pitchModSens->setSliderStyle (juce::Slider::RotaryVerticalDrag); pitchModSens->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -286,6 +303,7 @@ GlobalEditor::GlobalEditor () lfoSync.reset (new juce::ToggleButton ("lfoSync")); addAndMakeVisible (lfoSync.get()); + lfoSync->setExplicitFocusOrder (16); lfoSync->setButtonText (juce::String()); lfoSync->addListener (this); @@ -305,6 +323,7 @@ GlobalEditor::GlobalEditor () feedback.reset (new juce::Slider ("feedback")); addAndMakeVisible (feedback.get()); + feedback->setExplicitFocusOrder (12); feedback->setRange (0, 7, 1); feedback->setSliderStyle (juce::Slider::RotaryVerticalDrag); feedback->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -314,6 +333,7 @@ GlobalEditor::GlobalEditor () algo.reset (new juce::Slider ("algo")); addAndMakeVisible (algo.get()); + algo->setExplicitFocusOrder (11); algo->setRange (1, 32, 1); algo->setSliderStyle (juce::Slider::RotaryVerticalDrag); algo->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -329,6 +349,7 @@ GlobalEditor::GlobalEditor () output.reset (new juce::Slider ("output")); addAndMakeVisible (output.get()); + output->setExplicitFocusOrder (8); output->setRange (0, 1, 0); output->setSliderStyle (juce::Slider::RotaryVerticalDrag); output->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -336,9 +357,7 @@ GlobalEditor::GlobalEditor () output->setBounds (157, 60, 34, 34); - //vuOutput.reset (new VuMeter()); - //vuOutput.reset(new VuMeterMain(6)); - vuOutput.reset(new VuMeterOutput); + vuOutput.reset (new VuMeterOutput ()); addAndMakeVisible (vuOutput.get()); vuOutput->setName ("vuOutput"); @@ -346,34 +365,39 @@ GlobalEditor::GlobalEditor () initButton.reset (new juce::TextButton ("initButton")); addAndMakeVisible (initButton.get()); - initButton->setButtonText (translate("INIT")); + initButton->setExplicitFocusOrder (3); + initButton->setButtonText (TRANS ("INIT")); initButton->addListener (this); initButton->setBounds (100, 111, 50, 30); parmButton.reset (new juce::TextButton ("parmButton")); addAndMakeVisible (parmButton.get()); - parmButton->setButtonText (translate("PARM")); + parmButton->setExplicitFocusOrder (2); + parmButton->setButtonText (TRANS ("PARM")); parmButton->addListener (this); parmButton->setBounds (52, 111, 50, 30); cartButton.reset (new juce::TextButton ("cartButton")); addAndMakeVisible (cartButton.get()); - cartButton->setButtonText (translate("CART")); + cartButton->setExplicitFocusOrder (1); + cartButton->setButtonText (TRANS ("CART")); cartButton->addListener (this); cartButton->setBounds (3, 111, 50, 30); storeButton.reset (new juce::TextButton ("storeButton")); addAndMakeVisible (storeButton.get()); - storeButton->setButtonText (translate("STORE")); + storeButton->setExplicitFocusOrder (4); + storeButton->setButtonText (TRANS ("STORE")); storeButton->addListener (this); storeButton->setBounds (270, 109, 50, 30); monoMode.reset (new juce::ToggleButton ("monoMode")); addAndMakeVisible (monoMode.get()); + monoMode->setExplicitFocusOrder (10); monoMode->setButtonText (juce::String()); monoMode->addListener (this); @@ -381,6 +405,7 @@ GlobalEditor::GlobalEditor () lfoType.reset (new ComboBoxImage()); addAndMakeVisible (lfoType.get()); + lfoType->setExplicitFocusOrder (13); lfoType->setName ("lfoType"); lfoType->setBounds (583, 8, 36, 26); @@ -404,6 +429,7 @@ GlobalEditor::GlobalEditor () tune.reset (new juce::Slider ("tune")); addAndMakeVisible (tune.get()); + tune->setExplicitFocusOrder (5); tune->setRange (0, 1, 0); tune->setSliderStyle (juce::Slider::RotaryVerticalDrag); tune->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -791,86 +817,86 @@ BEGIN_JUCER_METADATA fixedSize="1" initialWidth="864" initialHeight="144"> + explicitFocusOrder="0" pos="6 103 140 8" class="VuMeterOutput" + params=""/> diff --git a/Source/GlobalEditor.h b/Source/GlobalEditor.h index 65b59b43..232ef68c 100644 --- a/Source/GlobalEditor.h +++ b/Source/GlobalEditor.h @@ -7,7 +7,7 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 6.0.7 + Created with Projucer version: 7.0.9 ------------------------------------------------------------------------------ diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index 9f82e6d9..d2270849 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -7,7 +7,7 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 6.0.1 + Created with Projucer version: 7.0.9 ------------------------------------------------------------------------------ @@ -19,7 +19,7 @@ //[Headers] You can add your own extra header files here... //[/Headers] -#include "VUMeter.h" + #include "OperatorEditor.h" @@ -52,6 +52,7 @@ OperatorEditor::OperatorEditor () s_egl1.reset (new juce::Slider ("egl1")); addAndMakeVisible (s_egl1.get()); + s_egl1->setExplicitFocusOrder (4); s_egl1->setRange (0, 99, 1); s_egl1->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egl1->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -61,15 +62,17 @@ OperatorEditor::OperatorEditor () s_egl2.reset (new juce::Slider ("egl2")); addAndMakeVisible (s_egl2.get()); + s_egl2->setExplicitFocusOrder (6); s_egl2->setRange (0, 99, 1); s_egl2->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egl2->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); s_egl2->addListener (this); - s_egl2->setBounds (33, 129, 34, 34); + s_egl2->setBounds (33, 128, 34, 34); s_egl3.reset (new juce::Slider ("egl3")); addAndMakeVisible (s_egl3.get()); + s_egl3->setExplicitFocusOrder (8); s_egl3->setRange (0, 99, 1); s_egl3->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egl3->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -79,6 +82,7 @@ OperatorEditor::OperatorEditor () s_egl4.reset (new juce::Slider ("egl4")); addAndMakeVisible (s_egl4.get()); + s_egl4->setExplicitFocusOrder (10); s_egl4->setRange (0, 99, 1); s_egl4->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egl4->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -88,6 +92,7 @@ OperatorEditor::OperatorEditor () s_egv1.reset (new juce::Slider ("egr1")); addAndMakeVisible (s_egv1.get()); + s_egv1->setExplicitFocusOrder (5); s_egv1->setRange (0, 99, 1); s_egv1->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egv1->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -97,6 +102,7 @@ OperatorEditor::OperatorEditor () s_egv2.reset (new juce::Slider ("egr3")); addAndMakeVisible (s_egv2.get()); + s_egv2->setExplicitFocusOrder (7); s_egv2->setRange (0, 99, 1); s_egv2->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egv2->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -106,6 +112,7 @@ OperatorEditor::OperatorEditor () s_egv3.reset (new juce::Slider ("egr3")); addAndMakeVisible (s_egv3.get()); + s_egv3->setExplicitFocusOrder (9); s_egv3->setRange (0, 99, 1); s_egv3->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egv3->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -115,6 +122,7 @@ OperatorEditor::OperatorEditor () s_egv4.reset (new juce::Slider ("egr4")); addAndMakeVisible (s_egv4.get()); + s_egv4->setExplicitFocusOrder (11); s_egv4->setRange (0, 99, 1); s_egv4->setSliderStyle (juce::Slider::RotaryVerticalDrag); s_egv4->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -124,6 +132,7 @@ OperatorEditor::OperatorEditor () opLevel.reset (new juce::Slider ("opLevel")); addAndMakeVisible (opLevel.get()); + opLevel->setExplicitFocusOrder (15); opLevel->setRange (0, 99, 1); opLevel->setSliderStyle (juce::Slider::RotaryVerticalDrag); opLevel->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -133,6 +142,7 @@ OperatorEditor::OperatorEditor () opFine.reset (new juce::Slider ("opFine")); addAndMakeVisible (opFine.get()); + opFine->setExplicitFocusOrder (3); opFine->setRange (0, 99, 1); opFine->setSliderStyle (juce::Slider::RotaryVerticalDrag); opFine->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -142,6 +152,7 @@ OperatorEditor::OperatorEditor () opCoarse.reset (new juce::Slider ("opCoarse")); addAndMakeVisible (opCoarse.get()); + opCoarse->setExplicitFocusOrder (2); opCoarse->setRange (0, 31, 1); opCoarse->setSliderStyle (juce::Slider::RotaryVerticalDrag); opCoarse->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -150,7 +161,7 @@ OperatorEditor::OperatorEditor () opCoarse->setBounds (43, 24, 34, 34); khzDisplay.reset (new juce::Label ("khz", - translate("1,000 kHz"))); + TRANS ("1,000 kHz"))); addAndMakeVisible (khzDisplay.get()); khzDisplay->setFont (juce::Font (12.60f, juce::Font::plain).withTypefaceStyle ("Regular")); khzDisplay->setJustificationType (juce::Justification::centred); @@ -165,6 +176,7 @@ OperatorEditor::OperatorEditor () detune.reset (new juce::Slider ("detune")); addAndMakeVisible (detune.get()); + detune->setExplicitFocusOrder (1); detune->setRange (-7, 7, 1); detune->setSliderStyle (juce::Slider::RotaryVerticalDrag); detune->setTextBoxStyle (juce::Slider::NoTextBox, true, 80, 20); @@ -180,7 +192,8 @@ OperatorEditor::OperatorEditor () sclLeftLevel.reset (new juce::Slider ("sclLeftLevel")); addAndMakeVisible (sclLeftLevel.get()); - sclLeftLevel->setTooltip (translate("Keyboard Scale Level Left Depth ")); + sclLeftLevel->setTooltip (TRANS ("Keyboard Scale Level Left Depth ")); + sclLeftLevel->setExplicitFocusOrder (16); sclLeftLevel->setRange (0, 99, 1); sclLeftLevel->setSliderStyle (juce::Slider::RotaryVerticalDrag); sclLeftLevel->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -190,7 +203,8 @@ OperatorEditor::OperatorEditor () sclRightLevel.reset (new juce::Slider ("sclRightLevel")); addAndMakeVisible (sclRightLevel.get()); - sclRightLevel->setTooltip (translate("Keyboard Scale Level Right Depth ")); + sclRightLevel->setTooltip (TRANS ("Keyboard Scale Level Right Depth ")); + sclRightLevel->setExplicitFocusOrder (18); sclRightLevel->setRange (0, 99, 1); sclRightLevel->setSliderStyle (juce::Slider::RotaryVerticalDrag); sclRightLevel->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -200,7 +214,8 @@ OperatorEditor::OperatorEditor () sclLvlBrkPt.reset (new juce::Slider ("sclLvlBrkPt")); addAndMakeVisible (sclLvlBrkPt.get()); - sclLvlBrkPt->setTooltip (translate("Scale Level Breakpoint")); + sclLvlBrkPt->setTooltip (TRANS ("Scale Level Breakpoint")); + sclLvlBrkPt->setExplicitFocusOrder (17); sclLvlBrkPt->setRange (0, 99, 1); sclLvlBrkPt->setSliderStyle (juce::Slider::LinearHorizontal); sclLvlBrkPt->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -210,7 +225,8 @@ OperatorEditor::OperatorEditor () sclRateScaling.reset (new juce::Slider ("sclRateScaling")); addAndMakeVisible (sclRateScaling.get()); - sclRateScaling->setTooltip (translate("Keyboard Rate Scaling")); + sclRateScaling->setTooltip (TRANS ("Keyboard Rate Scaling")); + sclRateScaling->setExplicitFocusOrder (20); sclRateScaling->setRange (0, 7, 1); sclRateScaling->setSliderStyle (juce::Slider::RotaryVerticalDrag); sclRateScaling->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -220,6 +236,7 @@ OperatorEditor::OperatorEditor () keyVelSens.reset (new juce::Slider ("keyVelSens")); addAndMakeVisible (keyVelSens.get()); + keyVelSens->setExplicitFocusOrder (14); keyVelSens->setRange (0, 7, 1); keyVelSens->setSliderStyle (juce::Slider::RotaryVerticalDrag); keyVelSens->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -229,6 +246,7 @@ OperatorEditor::OperatorEditor () ampModSens.reset (new juce::Slider ("ampModSens")); addAndMakeVisible (ampModSens.get()); + ampModSens->setExplicitFocusOrder (13); ampModSens->setRange (0, 3, 1); ampModSens->setSliderStyle (juce::Slider::RotaryVerticalDrag); ampModSens->setTextBoxStyle (juce::Slider::NoTextBox, false, 80, 20); @@ -244,6 +262,7 @@ OperatorEditor::OperatorEditor () opMode.reset (new juce::ToggleButton ("opMode")); addAndMakeVisible (opMode.get()); + opMode->setExplicitFocusOrder (12); opMode->setButtonText (juce::String()); opMode->addListener (this); @@ -251,12 +270,14 @@ OperatorEditor::OperatorEditor () kbdLeftCurve.reset (new ComboBoxImage()); addAndMakeVisible (kbdLeftCurve.get()); + kbdLeftCurve->setExplicitFocusOrder (19); kbdLeftCurve->setName ("kbdLeftCurve"); kbdLeftCurve->setBounds (128, 170, 36, 26); kbdRightCurve.reset (new ComboBoxImage()); addAndMakeVisible (kbdRightCurve.get()); + kbdRightCurve->setExplicitFocusOrder (21); kbdRightCurve->setName ("kbdRightCurve"); kbdRightCurve->setBounds (240, 170, 36, 26); @@ -619,47 +640,47 @@ BEGIN_JUCER_METADATA fixedSize="1" initialWidth="287" initialHeight="218"> + virtualName="" explicitFocusOrder="5" pos="236 136 90 24" editable="0" + layout="33" items="100 % 125 % 150 % 200 % 300 % 400 %" + textWhenNonSelected="" textWhenNoItems="(no choices)"/> END_JUCER_METADATA diff --git a/Source/ParamDialog.h b/Source/ParamDialog.h index fe918af9..8b322305 100644 --- a/Source/ParamDialog.h +++ b/Source/ParamDialog.h @@ -7,7 +7,7 @@ the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded and re-saved. - Created with Projucer version: 6.0.7 + Created with Projucer version: 7.0.9 ------------------------------------------------------------------------------ diff --git a/Source/PluginParam.cpp b/Source/PluginParam.cpp index c3461e81..4d1635d7 100644 --- a/Source/PluginParam.cpp +++ b/Source/PluginParam.cpp @@ -243,6 +243,8 @@ void Ctrl::bind(Slider *s) { s->addListener(this); s->addMouseListener(this, true); s->setVelocityModeParameters (0.1, 1, 0.05, 1, ModifierKeys::shiftModifier); + s->textFromValueFunction = [this](double value) { return this->label + this->getValueDisplay(); }; + s->setWantsKeyboardFocus(true); } void Ctrl::bind(Button *b) { From 325d43bfaaf8eed552bedeb2f4d4418fb617d58a Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sun, 11 Aug 2024 17:35:08 -0400 Subject: [PATCH 08/17] WIP Accessibility --- Source/CartManager.cpp | 20 +++++++++++++++----- Source/CartManager.h | 1 + Source/OperatorEditor.cpp | 4 ++++ Source/PluginEditor.cpp | 4 ++++ Source/PluginEditor.h | 3 ++- Source/PluginParam.h | 3 +++ Source/ProgramListBox.cpp | 1 + Source/ProgramListBox.h | 15 +++++++++++++++ libs/JUCE | 2 +- libs/MTS-ESP | 2 +- libs/clap-juce-extensions | 2 +- libs/tuning-library | 2 +- libs/vst3sdk | 2 +- 13 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Source/CartManager.cpp b/Source/CartManager.cpp index 1c32a5a6..6c09a692 100644 --- a/Source/CartManager.cpp +++ b/Source/CartManager.cpp @@ -121,11 +121,11 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan mainWindow = editor; cartDir = DexedAudioProcessor::dexedCartDir; - activeCart.reset(new ProgramListBox("activepgm", 8)); + activeCart.reset(new ProgramListBox("Active Programs Selector", 8)); addAndMakeVisible(activeCart.get()); activeCart->addListener(this); - browserCart.reset(new ProgramListBox("browserpgm", 2)); + browserCart.reset(new ProgramListBox("Browser Programs Selector", 2)); addAndMakeVisible(browserCart.get()); browserCart->addListener(this); @@ -140,6 +140,7 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan cartBrowser->addKeyListener(this); addAndMakeVisible(cartBrowser.get()); + cartBrowser->setTitle("Cartridge file browser"); cartBrowser->setDragAndDropDescription("Sysex Browser"); cartBrowser->addListener(this); @@ -234,8 +235,7 @@ void CartManager::programSelected(ProgramListBox *source, int pos) { void CartManager::buttonClicked(juce::Button *buttonThatWasClicked) { if ( buttonThatWasClicked == closeButton.get() ) { - mainWindow->startTimer(100); - getParentComponent()->setVisible(false); + hideCartridgeManager(); return; } @@ -411,8 +411,13 @@ void CartManager::initialFocus() { cartBrowser->grabKeyboardFocus(); } +void CartManager::hideCartridgeManager() { + mainWindow->startTimer(100); + getParentComponent()->setVisible(false); +} + bool CartManager::keyPressed(const KeyPress& key, Component* originatingComponent) { - if ( key.getKeyCode() == 13 ) { + if ( key.getKeyCode() == KeyPress::returnKey ) { File file = cartBrowser->getSelectedFile(); if ( file.isDirectory() ) return true; @@ -420,6 +425,11 @@ bool CartManager::keyPressed(const KeyPress& key, Component* originatingComponen activeCart->setCartridge(mainWindow->processor->currentCart); return true; } + if ( key.getKeyCode() == KeyPress::escapeKey ) { + hideCartridgeManager(); + return true; + } + return false; } diff --git a/Source/CartManager.h b/Source/CartManager.h index a5019379..38c28699 100644 --- a/Source/CartManager.h +++ b/Source/CartManager.h @@ -109,6 +109,7 @@ class CartManager : public Component, public Button::Listener, public DragAndDr virtual bool keyPressed(const KeyPress& key, Component* originatingComponent) override; void initialFocus(); + void hideCartridgeManager(); std::unique_ptr createFocusTraverser() override; }; diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index d2270849..6345978d 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -314,6 +314,10 @@ OperatorEditor::OperatorEditor () background = lookAndFeel->imageOperator; + opSwitch->setTitle("Operator switch"); + kbdLeftCurve->setTitle("Keyboard Left Curve"); + kbdRightCurve->setTitle("Keyboard Right Curve"); + //[/Constructor] } diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 582f83f5..99d4b0e7 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -115,6 +115,10 @@ void DexedAudioProcessorEditor::cartShow() { cartManager.initialFocus(); } +std::unique_ptr DexedAudioProcessorEditor::createFocusTraverser() { + return std::make_unique(); +} + void DexedAudioProcessorEditor::loadCart(File file) { Cartridge cart; diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index b380f9ad..e9ec245e 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -65,7 +65,8 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox: virtual bool isInterestedInFileDrag (const StringArray &files) override; virtual void filesDropped (const StringArray &files, int x, int y ) override; - + std::unique_ptr createFocusTraverser() override; + static const int WINDOW_SIZE_X = 866; static const int WINDOW_SIZE_Y = 674; }; diff --git a/Source/PluginParam.h b/Source/PluginParam.h index bc07742e..c814c3ad 100644 --- a/Source/PluginParam.h +++ b/Source/PluginParam.h @@ -23,6 +23,9 @@ #include "../JuceLibraryCode/JuceHeader.h" +#include +#include + class DexedAudioProcessor; class Ctrl : public Slider::Listener, public Button::Listener, public ComboBox::Listener, public MouseListener { diff --git a/Source/ProgramListBox.cpp b/Source/ProgramListBox.cpp index 635f6b1b..083aa4c2 100644 --- a/Source/ProgramListBox.cpp +++ b/Source/ProgramListBox.cpp @@ -31,6 +31,7 @@ ProgramListBox::ProgramListBox(const String name, int numCols) : Component(name) dragCandidate = -1; readOnly = false; programNames.clear(); + setTitle(name); setWantsKeyboardFocus(true); addKeyListener(this); } diff --git a/Source/ProgramListBox.h b/Source/ProgramListBox.h index 4aa831ed..d8ef95f1 100644 --- a/Source/ProgramListBox.h +++ b/Source/ProgramListBox.h @@ -69,6 +69,21 @@ class ProgramListBox : public Component, public DragAndDropTarget, public KeyLis void itemDropped(const SourceDetails& dragSourceDetails) override; bool keyPressed (const KeyPress& key, Component* originatingComponent) override; + + struct ProgramListBoxAH : public juce::AccessibilityHandler { + explicit ProgramListBoxAH(ProgramListBox *s): od(s), juce::AccessibilityHandler(*s, juce::AccessibilityRole::table, + juce::AccessibilityActions().addAction(juce::AccessibilityActionType::focus, + [this]() { })) { + } + + ProgramListBox *od; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProgramListBoxAH); + }; + + + std::unique_ptr< AccessibilityHandler > createAccessibilityHandler(ProgramListBox *programListBox) { + return std::make_unique(programListBox); + } }; diff --git a/libs/JUCE b/libs/JUCE index d054f0d1..22df0d22 160000 --- a/libs/JUCE +++ b/libs/JUCE @@ -1 +1 @@ -Subproject commit d054f0d14dcac387aebda44ce5d792b5e7a625b3 +Subproject commit 22df0d2266007bccb25d6ed52b9907f60d04e971 diff --git a/libs/MTS-ESP b/libs/MTS-ESP index 514ed958..803c3aab 160000 --- a/libs/MTS-ESP +++ b/libs/MTS-ESP @@ -1 +1 @@ -Subproject commit 514ed958b38e5ab501999f902a2427ebe6232a78 +Subproject commit 803c3aab3d43dfaea430a1084ab31b606f5cd72c diff --git a/libs/clap-juce-extensions b/libs/clap-juce-extensions index 4491bc30..2c23b918 160000 --- a/libs/clap-juce-extensions +++ b/libs/clap-juce-extensions @@ -1 +1 @@ -Subproject commit 4491bc30223cecf8ff495de943510863b71a7a81 +Subproject commit 2c23b918828ba5fbc5fcb4c95d3a046fbf7e9285 diff --git a/libs/tuning-library b/libs/tuning-library index 601c0eab..3bbe9514 160000 --- a/libs/tuning-library +++ b/libs/tuning-library @@ -1 +1 @@ -Subproject commit 601c0eabff31fff8a543b49d3e48073f338f413c +Subproject commit 3bbe9514816e1ae674c207b09e9f20eea4df372a diff --git a/libs/vst3sdk b/libs/vst3sdk index e9895dc9..56e4b2a6 160000 --- a/libs/vst3sdk +++ b/libs/vst3sdk @@ -1 +1 @@ -Subproject commit e9895dc9ef20bedd93a0fde5ad664bc8b56d4338 +Subproject commit 56e4b2a644be164c5d324e8bc9de55b964b0f102 From ee6a188fd99a35dc59911690c80fb6c9a75ddf44 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sun, 4 Aug 2024 16:38:48 -0400 Subject: [PATCH 09/17] Fix #328 load sysex bank from DAW --- Source/PluginProcessor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index d4ab38b8..08a9c8b1 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -613,7 +613,7 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid // test if it is a Yamaha Sysex if ( buf[1] != 0x43 ) { - TRACE("not a yamaha sysex %d", buf[0]); + TRACE("not a yamaha sysex %d", buf[1]); return; } @@ -669,8 +669,8 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid TRACE("unknown sysex substatus: %d", substatus); } - updateHostDisplay(); forceRefreshUI = true; + triggerAsyncUpdate(); } int DexedAudioProcessor::getEngineType() { From e32d5b791574673698a2a2db749baa83fbde0253 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sun, 11 Aug 2024 22:29:08 -0400 Subject: [PATCH 10/17] Fix #328 load sysex bank from DAW --- libs/JUCE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JUCE b/libs/JUCE index 22df0d22..ae514483 160000 --- a/libs/JUCE +++ b/libs/JUCE @@ -1 +1 @@ -Subproject commit 22df0d2266007bccb25d6ed52b9907f60d04e971 +Subproject commit ae5144833e852815d61642af87c69b9db44984f7 From 947ffa575ffa0b647da1bf832c1fbd965b0cd6d0 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Wed, 14 Aug 2024 21:10:01 -0400 Subject: [PATCH 11/17] Group operators for accessiblity --- README.md | 16 ++- Source/DXComponents.h | 6 +- Source/GlobalEditor.cpp | 3 + Source/OperatorEditor.cpp | 3 + Source/PluginData.h | 5 + Source/PluginParam.cpp | 243 +++++++++++++++++++------------------- Source/ProgramListBox.cpp | 2 +- Source/ProgramListBox.h | 15 +-- 8 files changed, 154 insertions(+), 139 deletions(-) diff --git a/README.md b/README.md index 7c950522..c88c96de 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,13 @@ the original machine. Dexed is licensed on the GPL v3. The msfa component (acronym for music synthesizer for android, see msfa in the source folder) stays on the Apache 2.0 license to able to collaborate between projects. +Donation +-------- +As a maintainer of this 10 year old project, donations are welcomed. This also applies for the Apple users +to cover for the notarization of this software in the comming years. Thank you! + +https://www.paypal.com/paypalme/asb2m10 + Dexed Forks ----------- * [MiniDexed](https://github.com/probonopd/MiniDexed) Run a DX7 bare metal from a Raspberry Pi @@ -22,16 +29,21 @@ Dexed Forks Changelog --------- +#### Version 0.9.8 (in development) +* Accessibility implementation +* Cartridge Manager UI refactoring (e.g. display .syx cartridge name) +* Mono/Poly parameter is now a plugin parameter +* Fix Logic startup issue + #### Version 0.9.7 * [MTS-ESP](https://oddsound.com/index.php) microtuning support * [CLAP](https://github.com/free-audio/clap) plugin support (sadly scaling is not available for now, but we are working on this) * Scalable UI upgrade (better resolution), optimized UI redraw * More accurate VU meter. Thanks @FulopNandor -* Releases are now notarized for mac OS +* Releases are now notarized for macOS * Fix for VST3 automation (again) * For developers: cmake is now the built system - #### Version 0.9.6 * Apple Silicon M1 builds * Fix VST3 automation issues diff --git a/Source/DXComponents.h b/Source/DXComponents.h index b503104c..4427e14c 100644 --- a/Source/DXComponents.h +++ b/Source/DXComponents.h @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2014 Pascal Gauthier. + * Copyright (c) 2014-2024 Pascal Gauthier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,6 +71,10 @@ class ComboBoxImage : public ComboBox { class ProgramSelector : public ComboBox { public: + ProgramSelector() { + setWantsKeyboardFocus(true); + setTitle("Program selector"); + } virtual void mouseDown(const MouseEvent &event) override; virtual void mouseEnter(const MouseEvent &event) override { accum_wheel = 0; } virtual void mouseWheelMove(const MouseEvent &event, const MouseWheelDetails &wheel) override; diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index fc62e594..d60af376 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -458,6 +458,9 @@ GlobalEditor::GlobalEditor () background = lookAndFeel->imageGlobal; imageLight = lookAndFeel->imageLight; + setTitle("Global Parameters"); + setFocusContainerType(FocusContainerType::focusContainer); + aboutButton->setTitle("About DEXED"); //[/Constructor] } diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index 6345978d..f549d688 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -315,6 +315,7 @@ OperatorEditor::OperatorEditor () background = lookAndFeel->imageOperator; opSwitch->setTitle("Operator switch"); + opMode->setTitle("Operator Mode"); kbdLeftCurve->setTitle("Keyboard Left Curve"); kbdRightCurve->setTitle("Keyboard Right Curve"); @@ -556,6 +557,8 @@ void OperatorEditor::bind(DexedAudioProcessor *parent, int op) { opNum << op + 1; internalOp = 5-op; + setTitle("Operator " + opNum); + setFocusContainerType(FocusContainerType::focusContainer); } void OperatorEditor::updateGain(float v) { diff --git a/Source/PluginData.h b/Source/PluginData.h index 68e29019..fc945e9a 100644 --- a/Source/PluginData.h +++ b/Source/PluginData.h @@ -236,6 +236,11 @@ class Cartridge { dest.add( normalizePgmName(getRawVoice() + ((i * 128) + 118)) ); } + String getProgramName(int idx) { + jassert(idx >= 0 && idx < 32); + return normalizePgmName(getRawVoice() + ((idx * 128) + 118)); + } + Cartridge operator =(const Cartridge other) { memcpy(voiceData, other.voiceData, SYSEX_SIZE); memcpy(perfData, other.perfData, SYSEX_SIZE); diff --git a/Source/PluginParam.cpp b/Source/PluginParam.cpp index 4d1635d7..b242122d 100644 --- a/Source/PluginParam.cpp +++ b/Source/PluginParam.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2013-2017 Pascal Gauthier. + * Copyright (c) 2013-2024 Pascal Gauthier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,127 @@ class CtrlUpdate : public CallbackMessage { } }; +// ************************************************************************ +// +Ctrl::Ctrl(String name) { + label << name; + slider = NULL; + button = NULL; + comboBox = NULL; +} + +void Ctrl::bind(Slider *s) { + slider = s; + updateComponent(); + s->addListener(this); + s->addMouseListener(this, true); + s->setVelocityModeParameters (0.1, 1, 0.05, 1, ModifierKeys::shiftModifier); + s->setTitle(label); + s->textFromValueFunction = [this](double value) { return this->getValueDisplay(); }; + s->setWantsKeyboardFocus(true); +} + +void Ctrl::bind(Button *b) { + button = b; + updateComponent(); + b->addListener(this); + b->addMouseListener(this, true); +} + +void Ctrl::bind(ComboBox *c) { + comboBox = c; + updateComponent(); + c->addListener(this); + c->addMouseListener(this, true); +} + +void Ctrl::unbind() { + if (slider != NULL) { + slider->removeListener(this); + slider->removeMouseListener(this); + slider = NULL; + } + + if (button != NULL) { + button->removeListener(this); + button->removeMouseListener(this); + button = NULL; + } + + if (comboBox != NULL) { + comboBox->removeListener(this); + comboBox->removeMouseListener(this); + comboBox = NULL; + } +} + +void Ctrl::publishValueAsync(float value) { + CtrlUpdate *update = new CtrlUpdate(this, value); + update->post(); +} + +void Ctrl::publishValue(float value) { + parent->beginParameterChangeGesture(idx); + parent->setParameterNotifyingHost(idx, value); + parent->endParameterChangeGesture(idx); +} + +void Ctrl::sliderValueChanged(Slider* moved) { + publishValue(moved->getValue()); +} + +void Ctrl::buttonClicked(Button* clicked) { + publishValue(clicked->getToggleState()); +} + +void Ctrl::comboBoxChanged(ComboBox* combo) { + publishValue((combo->getSelectedId() - 1) / combo->getNumItems()); +} + +void Ctrl::mouseEnter(const juce::MouseEvent &event) { + updateDisplayName(); +} + +void Ctrl::mouseDown(const juce::MouseEvent &event) { + if ( event.mods.isPopupMenu()) { + PopupMenu popup; + + if ( parent->mappedMidiCC.containsValue(this) ) { + popup.addItem(3, "Re-Map controller to midi CC for: " + String(label)); + popup.addSeparator(); + popup.addItem(1, "Remove midi CC mapping for this controller"); + } else { + popup.addItem(3, "Map controller to midi CC for: " + String(label)); + popup.addSeparator(); + } + popup.addItem(2, "Clear midi CC mapping"); + + switch(popup.show()) { + case 1: + parent->mappedMidiCC.removeValue(this); + parent->savePreference(); + break; + case 2: + if ( AlertWindow::showYesNoCancelBox(AlertWindow::WarningIcon, "Confirm", "Clear midi mapping for all controller change (CC) messages?", "YES", "NO", "CANCEL") ) { + parent->mappedMidiCC.clear(); + parent->savePreference(); + } + break; + case 3: + AudioProcessorEditor *editor = parent->getActiveEditor(); + if ( editor == NULL ) { + return; + } + DexedAudioProcessorEditor *dexedEditor = (DexedAudioProcessorEditor *) editor; + dexedEditor->discoverMidiCC(this); + break; + } + } +} + +void Ctrl::updateDisplayName() { +} + // ************************************************************************ // Custom displays @@ -228,126 +349,6 @@ class CtrlMonoPoly : public Ctrl { } }; -// ************************************************************************ -// -Ctrl::Ctrl(String name) { - label << name; - slider = NULL; - button = NULL; - comboBox = NULL; -} - -void Ctrl::bind(Slider *s) { - slider = s; - updateComponent(); - s->addListener(this); - s->addMouseListener(this, true); - s->setVelocityModeParameters (0.1, 1, 0.05, 1, ModifierKeys::shiftModifier); - s->textFromValueFunction = [this](double value) { return this->label + this->getValueDisplay(); }; - s->setWantsKeyboardFocus(true); -} - -void Ctrl::bind(Button *b) { - button = b; - updateComponent(); - b->addListener(this); - b->addMouseListener(this, true); -} - -void Ctrl::bind(ComboBox *c) { - comboBox = c; - updateComponent(); - c->addListener(this); - c->addMouseListener(this, true); -} - -void Ctrl::unbind() { - if (slider != NULL) { - slider->removeListener(this); - slider->removeMouseListener(this); - slider = NULL; - } - - if (button != NULL) { - button->removeListener(this); - button->removeMouseListener(this); - button = NULL; - } - - if (comboBox != NULL) { - comboBox->removeListener(this); - comboBox->removeMouseListener(this); - comboBox = NULL; - } -} - -void Ctrl::publishValueAsync(float value) { - CtrlUpdate *update = new CtrlUpdate(this, value); - update->post(); -} - -void Ctrl::publishValue(float value) { - parent->beginParameterChangeGesture(idx); - parent->setParameterNotifyingHost(idx, value); - parent->endParameterChangeGesture(idx); -} - -void Ctrl::sliderValueChanged(Slider* moved) { - publishValue(moved->getValue()); -} - -void Ctrl::buttonClicked(Button* clicked) { - publishValue(clicked->getToggleState()); -} - -void Ctrl::comboBoxChanged(ComboBox* combo) { - publishValue((combo->getSelectedId() - 1) / combo->getNumItems()); -} - -void Ctrl::mouseEnter(const juce::MouseEvent &event) { - updateDisplayName(); -} - -void Ctrl::mouseDown(const juce::MouseEvent &event) { - if ( event.mods.isPopupMenu()) { - PopupMenu popup; - - if ( parent->mappedMidiCC.containsValue(this) ) { - popup.addItem(3, "Re-Map controller to midi CC for: " + String(label)); - popup.addSeparator(); - popup.addItem(1, "Remove midi CC mapping for this controller"); - } else { - popup.addItem(3, "Map controller to midi CC for: " + String(label)); - popup.addSeparator(); - } - popup.addItem(2, "Clear midi CC mapping"); - - switch(popup.show()) { - case 1: - parent->mappedMidiCC.removeValue(this); - parent->savePreference(); - break; - case 2: - if ( AlertWindow::showYesNoCancelBox(AlertWindow::WarningIcon, "Confirm", "Clear midi mapping for all controller change (CC) messages?", "YES", "NO", "CANCEL") ) { - parent->mappedMidiCC.clear(); - parent->savePreference(); - } - break; - case 3: - AudioProcessorEditor *editor = parent->getActiveEditor(); - if ( editor == NULL ) { - return; - } - DexedAudioProcessorEditor *dexedEditor = (DexedAudioProcessorEditor *) editor; - dexedEditor->discoverMidiCC(this); - break; - } - } -} - -void Ctrl::updateDisplayName() { -} - // ************************************************************************ // CtrlFloat - control float values CtrlFloat::CtrlFloat(String name, float *storageValue) : Ctrl(name) { diff --git a/Source/ProgramListBox.cpp b/Source/ProgramListBox.cpp index 083aa4c2..4f10acb7 100644 --- a/Source/ProgramListBox.cpp +++ b/Source/ProgramListBox.cpp @@ -215,4 +215,4 @@ bool ProgramListBox::keyPressed(const KeyPress &key, Component *originatingCompo repaint(); return true; -} \ No newline at end of file +} diff --git a/Source/ProgramListBox.h b/Source/ProgramListBox.h index d8ef95f1..f7bfe461 100644 --- a/Source/ProgramListBox.h +++ b/Source/ProgramListBox.h @@ -70,20 +70,7 @@ class ProgramListBox : public Component, public DragAndDropTarget, public KeyLis bool keyPressed (const KeyPress& key, Component* originatingComponent) override; - struct ProgramListBoxAH : public juce::AccessibilityHandler { - explicit ProgramListBoxAH(ProgramListBox *s): od(s), juce::AccessibilityHandler(*s, juce::AccessibilityRole::table, - juce::AccessibilityActions().addAction(juce::AccessibilityActionType::focus, - [this]() { })) { - } - - ProgramListBox *od; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProgramListBoxAH); - }; - - - std::unique_ptr< AccessibilityHandler > createAccessibilityHandler(ProgramListBox *programListBox) { - return std::make_unique(programListBox); - } + std::unique_ptr< AccessibilityHandler > createAccessibilityHandler(ProgramListBox *programListBox); }; From 9c14258423d61cca08bafc49edb2c75f31941827 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Fri, 23 Aug 2024 14:51:47 -0400 Subject: [PATCH 12/17] WIP CartManager accessibility --- Documentation/Keybindings.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Documentation/Keybindings.md diff --git a/Documentation/Keybindings.md b/Documentation/Keybindings.md new file mode 100644 index 00000000..67d460a7 --- /dev/null +++ b/Documentation/Keybindings.md @@ -0,0 +1,8 @@ +# Dexed key bindings + +| Key bindings | Actions | +| ---------------------------- | -----------------------------| +| CTRL+1 to CTRL+6 | Focus on operator 'X' group | +| CTRL+SHIFT+1 TO CTRL+SHIFT+6 | Toggle active operator 'X' | +| CTRL+Q | Focus on global group | +| CTRL+L | Open cartridge manager | From 2cef88fa1f5f9203be11b39698ad51add4d45054 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Fri, 23 Aug 2024 14:56:29 -0400 Subject: [PATCH 13/17] 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); }; From e3d7ed3d6f74573e3b828f7245642d753d9cccf4 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Mon, 26 Aug 2024 23:42:23 -0400 Subject: [PATCH 14/17] Force CartManager focus traversial --- Documentation/Keybindings.md | 15 ++- Source/CartManager.cpp | 72 +++++++---- Source/CartManager.h | 2 +- Source/DXComponents.h | 102 +++++++++++++-- Source/DXLookNFeel.cpp | 4 +- Source/GlobalEditor.cpp | 1 - Source/OperatorEditor.cpp | 3 - Source/ParamDialog.cpp | 69 ++++++++++ Source/PluginEditor.cpp | 16 ++- Source/PluginEditor.h | 4 +- Source/PluginParam.cpp | 2 + Source/ProgramListBox.cpp | 244 +---------------------------------- Source/ProgramListBox.h | 168 +++++++++++++++++++++++- 13 files changed, 409 insertions(+), 293 deletions(-) diff --git a/Documentation/Keybindings.md b/Documentation/Keybindings.md index 67d460a7..b22d80cd 100644 --- a/Documentation/Keybindings.md +++ b/Documentation/Keybindings.md @@ -1,8 +1,11 @@ # Dexed key bindings -| Key bindings | Actions | -| ---------------------------- | -----------------------------| -| CTRL+1 to CTRL+6 | Focus on operator 'X' group | -| CTRL+SHIFT+1 TO CTRL+SHIFT+6 | Toggle active operator 'X' | -| CTRL+Q | Focus on global group | -| CTRL+L | Open cartridge manager | +| Key bindings | Actions | +| ---------------------------- | ------------------------------------------ | +| CTRL+1 to CTRL+6 | Focus on operator 'X' group | +| CTRL+SHIFT+1 TO CTRL+SHIFT+6 | Toggle active operator 'X' | +| CTRL+G | Focus on global group | +| CTRL+L | Open cartridge manager | +| CTRL+P | Open parameter dialog | +| CTRL+C | Copy current context on clipboard as hexa | +| CTRL+V | Paste context from clipboard content | diff --git a/Source/CartManager.cpp b/Source/CartManager.cpp index f80cf2e6..ae79a76f 100644 --- a/Source/CartManager.cpp +++ b/Source/CartManager.cpp @@ -87,29 +87,47 @@ class CartBrowserFocusTraverser : public KeyboardFocusTraverser { } Component* getNextComponent(Component* current) override { - for (int i=0;i(orders[i]); + if ( label != nullptr && !label->isActive() ) + continue; + break; } } - return root; + + if ( i == orders.size() ) + return orders.front(); + return orders[i]; } Component* getPreviousComponent(Component* current) override { - for (int i=0;i=0;i--) { if ( orders[i] == current ) { - i -= 1; - if ( i < 0 ) - return orders.back(); - else - return orders[i]; + srcFound = true; + continue; + } + if ( srcFound ) { + ProgramLabel *label = dynamic_cast(orders[i]); + if ( label != nullptr && !label->isActive() ) + continue; + break; } } - return root; + if ( i == -1 ) + return orders.back(); + return orders[i]; } std::vector getAllComponents(Component* parentComponent) override { @@ -164,13 +182,18 @@ CartManager::CartManager(DexedAudioProcessorEditor *editor) : Component("CartMan addAndMakeVisible(activeCartName.get()); focusOrder.push_back(cartBrowser.get()); - focusOrder.push_back(activeCart.get()); - focusOrder.push_back(browserCart.get()); + //focusOrder.push_back(browserCart.get()); + for(int i=0;i<32;i++) { + focusOrder.push_back(browserCart->getProgramComponent(i)); + } + //focusOrder.push_back(activeCart.get()); + for(int i=0;i<32;i++) { + focusOrder.push_back(activeCart->getProgramComponent(i)); + } focusOrder.push_back(closeButton.get()); focusOrder.push_back(loadButton.get()); focusOrder.push_back(saveButton.get()); focusOrder.push_back(fileMgrButton.get()); - focusOrder.push_back(activeCartName.get()); /* * @@ -192,16 +215,16 @@ CartManager::~CartManager() { cartBrowserList.reset(NULL); } -std::unique_ptr CartManager::createFocusTraverser() { +std::unique_ptr CartManager::createKeyboardFocusTraverser() { return std::make_unique(this, focusOrder); } void CartManager::resized() { - float activeSize = ((float) getWidth() - 30) / 8; + float activeSize = 100; - activeCart->setBounds(15, 402, activeSize * 8, 96); - browserCart->setBounds(activeSize * 6 + 15, 10, activeSize * 2, 384); - cartBrowser->setBounds(15, 10, activeSize * 6 - 1, 383); + activeCart->setBounds(14, 402, activeSize * 8, 96); + browserCart->setBounds(activeSize * 6 + 15, 10, activeSize * 2, 385); + cartBrowser->setBounds(14, 10, activeSize * 6 - 4, 385); closeButton->setBounds(4, getHeight() - 40, 70, 30); saveButton->setBounds(144, getHeight() - 40, 70, 30); loadButton->setBounds(74, getHeight() - 40, 70, 30); @@ -425,11 +448,6 @@ bool CartManager::keyPressed(const KeyPress& key, Component* originatingComponen activeCart->setCartridge(mainWindow->processor->currentCart); return true; } - if ( key.getKeyCode() == KeyPress::escapeKey ) { - hideCartridgeManager(); - return true; - } - return false; } diff --git a/Source/CartManager.h b/Source/CartManager.h index 38c28699..f90b5728 100644 --- a/Source/CartManager.h +++ b/Source/CartManager.h @@ -111,7 +111,7 @@ class CartManager : public Component, public Button::Listener, public DragAndDr void initialFocus(); void hideCartridgeManager(); - std::unique_ptr createFocusTraverser() override; + std::unique_ptr< ComponentTraverser> createKeyboardFocusTraverser() override; }; #endif // CARTMANAGER_H_INCLUDED diff --git a/Source/DXComponents.h b/Source/DXComponents.h index 4427e14c..c2e6301b 100644 --- a/Source/DXComponents.h +++ b/Source/DXComponents.h @@ -24,6 +24,65 @@ #include "../JuceLibraryCode/JuceHeader.h" #include +//============================================================================== +// THIS IS A TEMPORY FIX FOR COMBOBOX ACCESSIBILITY +// SEE: https://forum.juce.com/t/bug-combo-box-accessibility-bug/62501/2 +// WILL BE REMOVED ONCE WE UPDATE TO JUCE 8 +class ComboBoxAccessibilityHandlerFix final : public AccessibilityHandler +{ +public: + explicit ComboBoxAccessibilityHandlerFix (ComboBox& comboBoxToWrap) + : AccessibilityHandler (comboBoxToWrap, + AccessibilityRole::comboBox, + getAccessibilityActions (comboBoxToWrap), + { std::make_unique (comboBoxToWrap) }), + comboBox (comboBoxToWrap) + { + } + + AccessibleState getCurrentState() const override + { + auto state = AccessibilityHandler::getCurrentState().withExpandable(); + + return comboBox.isPopupActive() ? state.withExpanded() : state.withCollapsed(); + } + + String getTitle() const override { return comboBox.getTitle(); } // THE FIX IS RIGHT HERE + String getHelp() const override { return comboBox.getTooltip(); } + +private: + class ComboBoxValueInterface final : public AccessibilityTextValueInterface + { + public: + explicit ComboBoxValueInterface (ComboBox& comboBoxToWrap) + : comboBox (comboBoxToWrap) + { + } + + bool isReadOnly() const override { return true; } + String getCurrentValueAsString() const override { return comboBox.getText(); } + void setValueAsString (const String&) override {} + + private: + ComboBox& comboBox; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBoxValueInterface) + }; + + static AccessibilityActions getAccessibilityActions (ComboBox& comboBox) + { + return AccessibilityActions().addAction (AccessibilityActionType::press, [&comboBox] { comboBox.showPopup(); }) + .addAction (AccessibilityActionType::showMenu, [&comboBox] { comboBox.showPopup(); }); + } + + ComboBox& comboBox; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBoxAccessibilityHandlerFix) +}; +//============================================================================== + class EnvDisplay : public Component { public: EnvDisplay(); @@ -40,13 +99,7 @@ class PitchEnvDisplay : public Component { char vPos; void paint(Graphics &g); }; -/* -class VuMeter: public Component { - void paint(Graphics &g); -public : - float v; -}; -*/ + class LcdDisplay : public Component { public: LcdDisplay(); @@ -67,9 +120,15 @@ class ComboBoxImage : public ComboBox { virtual void showPopup() override; void setImage(Image image); void setImage(Image image, int pos[]); + + std::unique_ptr createAccessibilityHandler() override { + return std::make_unique (*this); + } + }; class ProgramSelector : public ComboBox { + float accum_wheel; public: ProgramSelector() { setWantsKeyboardFocus(true); @@ -80,8 +139,33 @@ class ProgramSelector : public ComboBox { virtual void mouseWheelMove(const MouseEvent &event, const MouseWheelDetails &wheel) override; virtual void paint(Graphics &g) override; -private: - float accum_wheel; + std::unique_ptr createAccessibilityHandler() override { + return std::make_unique (*this); + } +}; + +class FocusLogger final : public juce::FocusChangeListener +{ +public: + FocusLogger () + { + juce::Desktop::getInstance ().addFocusChangeListener (this); + } + + ~FocusLogger () override + { + juce::Desktop::getInstance ().removeFocusChangeListener (this); + } + + void globalFocusChanged (juce::Component * focusedComponent) override + { + if (focusedComponent == nullptr) + return; + + DBG ("Component title: " << focusedComponent->getTitle ()); + DBG ("Component type: " << typeid (*focusedComponent).name ()); + DBG ("---"); + } }; #endif // DXCOMPONENTS_H_INCLUDED diff --git a/Source/DXLookNFeel.cpp b/Source/DXLookNFeel.cpp index 41809579..23475b18 100644 --- a/Source/DXLookNFeel.cpp +++ b/Source/DXLookNFeel.cpp @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2013-2018 Pascal Gauthier. + * Copyright (c) 2013-2024 Pascal Gauthier. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -114,6 +114,8 @@ DXLookNFeel::DXLookNFeel() { REG_COLOUR(TreeView::backgroundColourId, background); REG_COLOUR(DirectoryContentsDisplayComponent::highlightColourId, fillColour); REG_COLOUR(DirectoryContentsDisplayComponent::textColourId, Colours::white); + REG_COLOUR(DialogWindow::backgroundColourId, background); + REG_COLOUR(ListBox::backgroundColourId, ctrlBackground); // Register ``Scrollbar::thumbColourId`` to allow its redefinion in ``DexedTheme.xml``. REG_COLOUR(ScrollBar::thumbColourId, background.darker()); diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index 6209a489..89b1ec85 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -453,7 +453,6 @@ GlobalEditor::GlobalEditor () lfoType->addItem("SINE", 5); lfoType->addItem("S&HOLD", 6); lfoType->setImage(lookAndFeel->imageLFO); - lfoType->setTitle("LFO Waveform"); programs = programSelector.get(); diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index c1d8610d..7484055b 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -314,9 +314,6 @@ OperatorEditor::OperatorEditor () background = lookAndFeel->imageOperator; opSwitch->setTitle("Operator switch"); - opMode->setTitle("Operator Mode"); - kbdLeftCurve->setTitle("Keyboard Left Curve"); - kbdRightCurve->setTitle("Keyboard Right Curve"); setWantsKeyboardFocus(true); //[/Constructor] diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp index 9d48222a..5045b62b 100644 --- a/Source/ParamDialog.cpp +++ b/Source/ParamDialog.cpp @@ -363,6 +363,75 @@ ParamDialog::ParamDialog () if ( JUCEApplication::isStandaloneApp() ) { sysexIn->setVisible(false); } + + // ACCESSIBLITY + pitchRangeUp->setTitle("Pitch Bend Range Up"); + pitchRangeDn->setTitle("Pitch Bend Range Down"); + pitchStep->setTitle("Pitch Bend Step"); + sysexIn->setTitle("Sysex Input"); + sysexOut->setTitle("Sysex Output"); + sysexChl->setTitle("Sysex Channel"); + engineReso->setTitle("Engine Resolution"); + showKeyboard->setTitle("Show Keyboard"); + whlRange->setTitle("Wheel Range"); + ftRange->setTitle("Foot Range"); + brRange->setTitle("Breath Range"); + atRange->setTitle("After Touch Range"); + whlEg->setTitle("Wheel EG"); + ftEg->setTitle("Foot EG"); + brEg->setTitle("Breath EG"); + atEg->setTitle("After Touch EG"); + whlAmp->setTitle("Wheel Amp"); + ftAmp->setTitle("Foot Amp"); + brAmp->setTitle("Breath Amp"); + atAmp->setTitle("After Touch Amp"); + whlPitch->setTitle("Wheel Pitch"); + ftPitch->setTitle("Foot Pitch"); + brPitch->setTitle("Breath Pitch"); + atPitch->setTitle("After Touch Pitch"); + sclButton->setTitle("Scale Button"); + kbmButton->setTitle("Keyboard Mapping Button"); + showTunButton->setTitle("Show Tuning Button"); + resetTuningButton->setTitle("Reset Tuning Button"); + transposeScale->setTitle("Transpose Scale"); + mpePBRange->setTitle("MPE Pitch Bend Range"); + mpeEnabled->setTitle("MPE Enabled"); + transposeHelp->setTitle("Transpose Help"); + scalingFactor->setTitle("Scaling Factor"); + + pitchRangeUp->setWantsKeyboardFocus(true); + pitchRangeDn->setWantsKeyboardFocus(true); + pitchStep->setWantsKeyboardFocus(true); + sysexIn->setWantsKeyboardFocus(true); + sysexOut->setWantsKeyboardFocus(true); + sysexChl->setWantsKeyboardFocus(true); + engineReso->setWantsKeyboardFocus(true); + showKeyboard->setWantsKeyboardFocus(true); + whlRange->setWantsKeyboardFocus(true); + ftRange->setWantsKeyboardFocus(true); + brRange->setWantsKeyboardFocus(true); + atRange->setWantsKeyboardFocus(true); + whlEg->setWantsKeyboardFocus(true); + ftEg->setWantsKeyboardFocus(true); + brEg->setWantsKeyboardFocus(true); + atEg->setWantsKeyboardFocus(true); + whlAmp->setWantsKeyboardFocus(true); + ftAmp->setWantsKeyboardFocus(true); + brAmp->setWantsKeyboardFocus(true); + atAmp->setWantsKeyboardFocus(true); + whlPitch->setWantsKeyboardFocus(true); + ftPitch->setWantsKeyboardFocus(true); + brPitch->setWantsKeyboardFocus(true); + atPitch->setWantsKeyboardFocus(true); + sclButton->setWantsKeyboardFocus(true); + kbmButton->setWantsKeyboardFocus(true); + showTunButton->setWantsKeyboardFocus(true); + resetTuningButton->setWantsKeyboardFocus(true); + transposeScale->setWantsKeyboardFocus(true); + mpePBRange->setWantsKeyboardFocus(true); + mpeEnabled->setWantsKeyboardFocus(true); + transposeHelp->setWantsKeyboardFocus(true); + scalingFactor->setWantsKeyboardFocus(true); //[/Constructor] } diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index a3e6cdbf..47ec4ccf 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -73,8 +73,8 @@ DexedAudioProcessorEditor::DexedAudioProcessorEditor (DexedAudioProcessor* owner // The DX7 is a badass on the bass, keep it that way midiKeyboard.setLowestVisibleKey(24); - midiKeyboard.setBounds(4, 581, getWidth() - 8, 90); + midiKeyboard.setTitle("Keyboard keys"); addAndMakeVisible(&global); global.setBounds(2,436,864,144); @@ -522,7 +522,9 @@ bool DexedAudioProcessorEditor::keyPressed(const KeyPress& key, Component* origi int keycode = key.getKeyCode(); ModifierKeys mods = key.getModifiers(); - TRACE("key pressed: %d\n", keycode); + #ifdef DEXED_EVENT_DEBUG + TRACE("key pressed: %d\n", keycode); + #endif if ( (keycode >= '1' && keycode <= '6') && mods.isCtrlDown() ) { int op = keycode - '1'; @@ -546,5 +548,15 @@ bool DexedAudioProcessorEditor::keyPressed(const KeyPress& key, Component* origi return true; } + if ( keycode == 'P' && mods.isCtrlDown() ) { + parmShow(); + return true; + } + + if ( key.getKeyCode() == KeyPress::escapeKey ) { + cartManager.hideCartridgeManager(); + return true; + } + return false; } \ No newline at end of file diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 4fd1726b..144b21be 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -41,7 +41,9 @@ class DexedAudioProcessorEditor : public AudioProcessorEditor, public ComboBox: Component cartManagerCover; SharedResourcePointer lookAndFeel; - + #ifdef DEXED_EVENT_DEBUG + FocusLogger focusLogger; + #endif public: DexedAudioProcessor *processor; GlobalEditor global; diff --git a/Source/PluginParam.cpp b/Source/PluginParam.cpp index b242122d..e4f7f83b 100644 --- a/Source/PluginParam.cpp +++ b/Source/PluginParam.cpp @@ -63,6 +63,7 @@ void Ctrl::bind(Slider *s) { void Ctrl::bind(Button *b) { button = b; updateComponent(); + b->setTitle(label); b->addListener(this); b->addMouseListener(this, true); } @@ -70,6 +71,7 @@ void Ctrl::bind(Button *b) { void Ctrl::bind(ComboBox *c) { comboBox = c; updateComponent(); + c->setTitle(label); c->addListener(this); c->addMouseListener(this, true); } diff --git a/Source/ProgramListBox.cpp b/Source/ProgramListBox.cpp index 20e43ad0..fb196c04 100644 --- a/Source/ProgramListBox.cpp +++ b/Source/ProgramListBox.cpp @@ -23,150 +23,6 @@ #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; @@ -187,13 +43,13 @@ ProgramListBox::ProgramListBox(const String name, int numCols) : Component(name) } void ProgramListBox::resized() { - cellWidth = getWidth() / cols; - cellHeight = getHeight() / rows; + cellWidth = (float) getWidth() / cols; + cellHeight = (float) 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); + labels[i].get()->setBounds(cellWidth*targetCols+1, cellHeight*targetRow+1, cellWidth-2, cellHeight-2); } } @@ -258,98 +114,6 @@ bool ProgramListBox::keyPressed(const KeyPress &key, Component *originatingCompo 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)); - g.drawLine(0,0,getWidth(), 0, 2); - g.setColour(Colour(3,3,1)); - g.drawLine(0,0,0,getHeight(),2); - g.setColour(Colour(34,32,32)); - g.drawLine(getWidth(), 3, getWidth(), getHeight(), 2); - g.setColour(Colour(75,73,73)); - g.drawLine(0,getHeight(),getWidth(),getHeight(), 2); - - const float dashLength[] = { 4, 4 }; - - g.setColour(Colour(83,76,69)); - for(int i=1;i line(cellWidth*i,0,cellWidth*i,getHeight()); - g.drawDashedLine(line, dashLength, 2); - } - for(int i=1;i line(2, cellHeight*i,getWidth(),cellHeight*i); - g.drawDashedLine(line, dashLength, 2); - } - */ - /* - for(int i=0;i(comp) == nullptr ) - return false; - - return true; -}*/ -/* -void ProgramListBox::itemDropped(const SourceDetails& dragSourceDetails) { - dragCandidate = programPosition(dragSourceDetails.localPosition.x, dragSourceDetails.localPosition.y); - - MemoryBlock* block = dragSourceDetails.description.getBinaryData(); - if ( listener != nullptr ) - listener->programDragged(this, dragCandidate, (char *)block->getData()); - dragCandidate = -1; - repaint(); -} - -void ProgramListBox::itemDragEnter(const SourceDetails &dragSourceDetails) { - dragCandidate = programPosition(dragSourceDetails.localPosition.x, dragSourceDetails.localPosition.y); - repaint(); -} - -void ProgramListBox::itemDragMove(const SourceDetails &dragSourceDetails) { - dragCandidate = programPosition(dragSourceDetails.localPosition.x, dragSourceDetails.localPosition.y); - repaint(); -} - -void ProgramListBox::itemDragExit(const SourceDetails &dragSourceDetails) { - dragCandidate = -1; - repaint(); + g.fillAll(DXLookNFeel::background); } -*/ diff --git a/Source/ProgramListBox.h b/Source/ProgramListBox.h index 0bd92977..5336d6ca 100644 --- a/Source/ProgramListBox.h +++ b/Source/ProgramListBox.h @@ -23,6 +23,7 @@ #include "JuceHeader.h" #include "PluginData.h" +#include "DXLookNFeel.h" class ProgramListBox; class ProgramListBoxListener { @@ -33,14 +34,15 @@ class ProgramListBoxListener { virtual void programDragged(ProgramListBox *destListBox, int dest, char *packedPgm) = 0; }; +class ProgramLabel; class ProgramListBox : public Component, public KeyListener { ProgramListBoxListener *listener; Cartridge cartContent; - std::unique_ptr labels[32]; + std::unique_ptr labels[32]; bool hasContent; int cols, rows; - int cellWidth, cellHeight; + float cellWidth, cellHeight; int activePgm; friend class ProgramLabel; @@ -51,12 +53,174 @@ class ProgramListBox : public Component, public KeyListener { ProgramListBox(const String name, int numCols); void addListener(ProgramListBoxListener *listener); void resized() override; + void paint(Graphics &g) override; void setActive(int idx); Cartridge &getCurrentCart(); void setCartridge(Cartridge &cart); bool keyPressed (const KeyPress& key, Component* originatingComponent) override; + + ProgramLabel *getProgramComponent(int idx) { + return labels[idx].get(); + } }; +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.75f); + g.fillAll(alternateColour); + } else { + auto alternateColour = DXLookNFeel::lightBackground.interpolatedWith (getLookAndFeel().findColour(ListBox::textColourId), 0.15f); + g.fillAll(alternateColour); + } + } + + 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 loadProgram() { + if ( ! pgmListBox->hasContent ) + return; + pgmListBox->listener->programSelected(pgmListBox, 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 ) + loadProgram(); + } + + 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(); + } + + bool isActive() { + return pgmListBox->hasContent; + } + + struct ProgramLabelAH : public juce::AccessibilityHandler { + explicit ProgramLabelAH(ProgramLabel *s): program(s), juce::AccessibilityHandler(*s, juce::AccessibilityRole::cell, getAccessibilityActions(s)) { + } + + virtual String getTitle() const override { + return String(program->idx + 1) + " " + program->getProgramName(); + } + + ProgramLabel *program; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProgramLabelAH); + + static AccessibilityActions getAccessibilityActions (ProgramLabel* label) { + return AccessibilityActions().addAction(AccessibilityActionType::press, [label] { label->loadProgram(); }); + } + }; + + std::unique_ptr< AccessibilityHandler> createAccessibilityHandler() override { + return std::make_unique(this); + } +}; #endif // PROGRAMLISTBOX_H_INCLUDED From a84649dae1df716f0a2922993c00db6b76061038 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sat, 28 Sep 2024 18:13:14 -0400 Subject: [PATCH 15/17] If shift is pressed, make 10% jump on sliders --- Source/DXComponents.h | 25 ++++++++++++++++++++++++ Source/GlobalEditor.cpp | 40 +++++++++++++++++++-------------------- Source/OperatorEditor.cpp | 36 +++++++++++++++++------------------ Source/ParamDialog.cpp | 2 ++ 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/Source/DXComponents.h b/Source/DXComponents.h index c2e6301b..49ca8e7c 100644 --- a/Source/DXComponents.h +++ b/Source/DXComponents.h @@ -144,6 +144,31 @@ class ProgramSelector : public ComboBox { } }; +// Simple Slider to make 10 % jumps when shift is pressed +class DXSlider : public Slider { +public: + DXSlider(const String& componentName) : Slider(componentName) { + setWantsKeyboardFocus(true); + } + + bool keyPressed(const KeyPress &key) override { + if ( key.getModifiers().isShiftDown() ) { + float len = getRange().getLength() * 0.10; + if (key.getKeyCode() == key.upKey) { + setValue(getValue() + len); + return true; + } + if (key.getKeyCode() == key.downKey) { + setValue(getValue() - len); + return true; + } + } else { + return Slider::keyPressed(key); + } + return false; + } +}; + class FocusLogger final : public juce::FocusChangeListener { public: diff --git a/Source/GlobalEditor.cpp b/Source/GlobalEditor.cpp index 89b1ec85..ca5c494e 100644 --- a/Source/GlobalEditor.cpp +++ b/Source/GlobalEditor.cpp @@ -133,7 +133,7 @@ GlobalEditor::GlobalEditor () //[Constructor_pre] You can add your own custom stuff here.. //[/Constructor_pre] - lfoSpeed.reset (new juce::Slider ("lfoSpeed")); + lfoSpeed.reset (new DXSlider ("lfoSpeed")); addAndMakeVisible (lfoSpeed.get()); lfoSpeed->setExplicitFocusOrder (14); lfoSpeed->setRange (0, 99, 1); @@ -143,7 +143,7 @@ GlobalEditor::GlobalEditor () lfoSpeed->setBounds (564, 50, 34, 34); - lfoAmDepth.reset (new juce::Slider ("lfoAmDepth")); + lfoAmDepth.reset (new DXSlider ("lfoAmDepth")); addAndMakeVisible (lfoAmDepth.get()); lfoAmDepth->setExplicitFocusOrder (19); lfoAmDepth->setRange (0, 99, 1); @@ -153,7 +153,7 @@ GlobalEditor::GlobalEditor () lfoAmDepth->setBounds (686, 50, 34, 34); - lfoPitchDepth.reset (new juce::Slider ("lfoPitchDepth")); + lfoPitchDepth.reset (new DXSlider ("lfoPitchDepth")); addAndMakeVisible (lfoPitchDepth.get()); lfoPitchDepth->setExplicitFocusOrder (18); lfoPitchDepth->setRange (0, 99, 1); @@ -163,7 +163,7 @@ GlobalEditor::GlobalEditor () lfoPitchDepth->setBounds (646, 50, 34, 34); - lfoDelay.reset (new juce::Slider ("lfoDelay")); + lfoDelay.reset (new DXSlider ("lfoDelay")); addAndMakeVisible (lfoDelay.get()); lfoDelay->setExplicitFocusOrder (15); lfoDelay->setRange (0, 99, 1); @@ -173,7 +173,7 @@ GlobalEditor::GlobalEditor () lfoDelay->setBounds (603, 50, 34, 34); - cutoff.reset (new juce::Slider ("cutoff")); + cutoff.reset (new DXSlider ("cutoff")); addAndMakeVisible (cutoff.get()); cutoff->setExplicitFocusOrder (6); cutoff->setRange (0, 1, 0); @@ -183,7 +183,7 @@ GlobalEditor::GlobalEditor () cutoff->setBounds (234, 9, 34, 34); - reso.reset (new juce::Slider ("reso")); + reso.reset (new DXSlider ("reso")); addAndMakeVisible (reso.get()); reso->setExplicitFocusOrder (7); reso->setRange (0, 1, 0); @@ -193,7 +193,7 @@ GlobalEditor::GlobalEditor () reso->setBounds (278, 9, 34, 34); - pitchRate2.reset (new juce::Slider ("pitchRate2")); + pitchRate2.reset (new DXSlider ("pitchRate2")); addAndMakeVisible (pitchRate2.get()); pitchRate2->setExplicitFocusOrder (24); pitchRate2->setRange (0, 99, 1); @@ -203,7 +203,7 @@ GlobalEditor::GlobalEditor () pitchRate2->setBounds (767, 96, 34, 34); - pitchRate3.reset (new juce::Slider ("pitchRate3")); + pitchRate3.reset (new DXSlider ("pitchRate3")); addAndMakeVisible (pitchRate3.get()); pitchRate3->setExplicitFocusOrder (26); pitchRate3->setRange (0, 99, 1); @@ -213,7 +213,7 @@ GlobalEditor::GlobalEditor () pitchRate3->setBounds (795, 96, 35, 34); - pitchRate4.reset (new juce::Slider ("pitchRate4")); + pitchRate4.reset (new DXSlider ("pitchRate4")); addAndMakeVisible (pitchRate4.get()); pitchRate4->setExplicitFocusOrder (28); pitchRate4->setRange (0, 99, 1); @@ -223,7 +223,7 @@ GlobalEditor::GlobalEditor () pitchRate4->setBounds (823, 96, 34, 34); - pitchRate1.reset (new juce::Slider ("pitchRate1")); + pitchRate1.reset (new DXSlider ("pitchRate1")); addAndMakeVisible (pitchRate1.get()); pitchRate1->setExplicitFocusOrder (22); pitchRate1->setRange (0, 99, 1); @@ -233,7 +233,7 @@ GlobalEditor::GlobalEditor () pitchRate1->setBounds (739, 96, 34, 34); - pitchLevel2.reset (new juce::Slider ("pitchLevel2")); + pitchLevel2.reset (new DXSlider ("pitchLevel2")); addAndMakeVisible (pitchLevel2.get()); pitchLevel2->setExplicitFocusOrder (23); pitchLevel2->setRange (0, 99, 1); @@ -243,7 +243,7 @@ GlobalEditor::GlobalEditor () pitchLevel2->setBounds (767, 57, 34, 34); - pitchLevel3.reset (new juce::Slider ("pitchLevel3")); + pitchLevel3.reset (new DXSlider ("pitchLevel3")); addAndMakeVisible (pitchLevel3.get()); pitchLevel3->setExplicitFocusOrder (25); pitchLevel3->setRange (0, 99, 1); @@ -253,7 +253,7 @@ GlobalEditor::GlobalEditor () pitchLevel3->setBounds (795, 56, 34, 34); - pitchLevel4.reset (new juce::Slider ("pitchLevel4")); + pitchLevel4.reset (new DXSlider ("pitchLevel4")); addAndMakeVisible (pitchLevel4.get()); pitchLevel4->setExplicitFocusOrder (27); pitchLevel4->setRange (0, 99, 1); @@ -263,7 +263,7 @@ GlobalEditor::GlobalEditor () pitchLevel4->setBounds (823, 56, 34, 34); - pitchLevel1.reset (new juce::Slider ("pitchLevel1")); + pitchLevel1.reset (new DXSlider ("pitchLevel1")); addAndMakeVisible (pitchLevel1.get()); pitchLevel1->setExplicitFocusOrder (21); pitchLevel1->setRange (0, 99, 1); @@ -273,7 +273,7 @@ GlobalEditor::GlobalEditor () pitchLevel1->setBounds (739, 57, 34, 34); - transpose.reset (new juce::Slider ("transpose")); + transpose.reset (new DXSlider ("transpose")); addAndMakeVisible (transpose.get()); transpose->setExplicitFocusOrder (9); transpose->setRange (0, 48, 1); @@ -291,7 +291,7 @@ GlobalEditor::GlobalEditor () oscSync->setBounds (650, 96, 48, 26); - pitchModSens.reset (new juce::Slider ("pitchModSens")); + pitchModSens.reset (new DXSlider ("pitchModSens")); addAndMakeVisible (pitchModSens.get()); pitchModSens->setExplicitFocusOrder (17); pitchModSens->setRange (0, 7, 1); @@ -321,7 +321,7 @@ GlobalEditor::GlobalEditor () algoDisplay->setBounds (335, 30, 152, 91); - feedback.reset (new juce::Slider ("feedback")); + feedback.reset (new DXSlider ("feedback")); addAndMakeVisible (feedback.get()); feedback->setExplicitFocusOrder (12); feedback->setRange (0, 7, 1); @@ -331,7 +331,7 @@ GlobalEditor::GlobalEditor () feedback->setBounds (501, 81, 34, 34); - algo.reset (new juce::Slider ("algo")); + algo.reset (new DXSlider ("algo")); addAndMakeVisible (algo.get()); algo->setExplicitFocusOrder (11); algo->setRange (1, 32, 1); @@ -347,7 +347,7 @@ GlobalEditor::GlobalEditor () lcdDisplay->setBounds (6, 87, 140, 13); - output.reset (new juce::Slider ("output")); + output.reset (new DXSlider ("output")); addAndMakeVisible (output.get()); output->setExplicitFocusOrder (8); output->setRange (0, 1, 0); @@ -427,7 +427,7 @@ GlobalEditor::GlobalEditor () juce::Image(), 1.000f, juce::Colour (0x00000000)); aboutButton->setBounds (8, 11, 135, 46); - tune.reset (new juce::Slider ("tune")); + tune.reset (new DXSlider ("tune")); addAndMakeVisible (tune.get()); tune->setExplicitFocusOrder (5); tune->setRange (0, 1, 0); diff --git a/Source/OperatorEditor.cpp b/Source/OperatorEditor.cpp index 7484055b..13a872c1 100644 --- a/Source/OperatorEditor.cpp +++ b/Source/OperatorEditor.cpp @@ -50,7 +50,7 @@ OperatorEditor::OperatorEditor () //[Constructor_pre] You can add your own custom stuff here.. //[/Constructor_pre] - s_egl1.reset (new juce::Slider ("egl1")); + s_egl1.reset (new DXSlider ("egl1")); addAndMakeVisible (s_egl1.get()); s_egl1->setExplicitFocusOrder (4); s_egl1->setRange (0, 99, 1); @@ -60,7 +60,7 @@ OperatorEditor::OperatorEditor () s_egl1->setBounds (5, 128, 34, 34); - s_egl2.reset (new juce::Slider ("egl2")); + s_egl2.reset (new DXSlider ("egl2")); addAndMakeVisible (s_egl2.get()); s_egl2->setExplicitFocusOrder (6); s_egl2->setRange (0, 99, 1); @@ -70,7 +70,7 @@ OperatorEditor::OperatorEditor () s_egl2->setBounds (33, 128, 34, 34); - s_egl3.reset (new juce::Slider ("egl3")); + s_egl3.reset (new DXSlider ("egl3")); addAndMakeVisible (s_egl3.get()); s_egl3->setExplicitFocusOrder (8); s_egl3->setRange (0, 99, 1); @@ -80,7 +80,7 @@ OperatorEditor::OperatorEditor () s_egl3->setBounds (61, 128, 34, 34); - s_egl4.reset (new juce::Slider ("egl4")); + s_egl4.reset (new DXSlider ("egl4")); addAndMakeVisible (s_egl4.get()); s_egl4->setExplicitFocusOrder (10); s_egl4->setRange (0, 99, 1); @@ -90,7 +90,7 @@ OperatorEditor::OperatorEditor () s_egl4->setBounds (89, 128, 34, 34); - s_egv1.reset (new juce::Slider ("egr1")); + s_egv1.reset (new DXSlider ("egr1")); addAndMakeVisible (s_egv1.get()); s_egv1->setExplicitFocusOrder (5); s_egv1->setRange (0, 99, 1); @@ -100,7 +100,7 @@ OperatorEditor::OperatorEditor () s_egv1->setBounds (5, 169, 34, 34); - s_egv2.reset (new juce::Slider ("egr3")); + s_egv2.reset (new DXSlider ("egr3")); addAndMakeVisible (s_egv2.get()); s_egv2->setExplicitFocusOrder (7); s_egv2->setRange (0, 99, 1); @@ -110,7 +110,7 @@ OperatorEditor::OperatorEditor () s_egv2->setBounds (33, 169, 34, 34); - s_egv3.reset (new juce::Slider ("egr3")); + s_egv3.reset (new DXSlider ("egr3")); addAndMakeVisible (s_egv3.get()); s_egv3->setExplicitFocusOrder (9); s_egv3->setRange (0, 99, 1); @@ -120,7 +120,7 @@ OperatorEditor::OperatorEditor () s_egv3->setBounds (61, 169, 34, 34); - s_egv4.reset (new juce::Slider ("egr4")); + s_egv4.reset (new DXSlider ("egr4")); addAndMakeVisible (s_egv4.get()); s_egv4->setExplicitFocusOrder (11); s_egv4->setRange (0, 99, 1); @@ -130,7 +130,7 @@ OperatorEditor::OperatorEditor () s_egv4->setBounds (89, 169, 34, 34); - opLevel.reset (new juce::Slider ("opLevel")); + opLevel.reset (new DXSlider ("opLevel")); addAndMakeVisible (opLevel.get()); opLevel->setExplicitFocusOrder (15); opLevel->setRange (0, 99, 1); @@ -140,7 +140,7 @@ OperatorEditor::OperatorEditor () opLevel->setBounds (245, 76, 34, 34); - opFine.reset (new juce::Slider ("opFine")); + opFine.reset (new DXSlider ("opFine")); addAndMakeVisible (opFine.get()); opFine->setExplicitFocusOrder (3); opFine->setRange (0, 99, 1); @@ -150,7 +150,7 @@ OperatorEditor::OperatorEditor () opFine->setBounds (78, 24, 34, 34); - opCoarse.reset (new juce::Slider ("opCoarse")); + opCoarse.reset (new DXSlider ("opCoarse")); addAndMakeVisible (opCoarse.get()); opCoarse->setExplicitFocusOrder (2); opCoarse->setRange (0, 31, 1); @@ -174,7 +174,7 @@ OperatorEditor::OperatorEditor () khzDisplay->setBounds (15, 10, 95, 10); - detune.reset (new juce::Slider ("detune")); + detune.reset (new DXSlider ("detune")); addAndMakeVisible (detune.get()); detune->setExplicitFocusOrder (1); detune->setRange (-7, 7, 1); @@ -190,7 +190,7 @@ OperatorEditor::OperatorEditor () envDisplay->setBounds (16, 83, 94, 30); - sclLeftLevel.reset (new juce::Slider ("sclLeftLevel")); + sclLeftLevel.reset (new DXSlider ("sclLeftLevel")); addAndMakeVisible (sclLeftLevel.get()); sclLeftLevel->setTooltip (TRANS ("Keyboard Scale Level Left Depth ")); sclLeftLevel->setExplicitFocusOrder (16); @@ -201,7 +201,7 @@ OperatorEditor::OperatorEditor () sclLeftLevel->setBounds (131, 115, 34, 34); - sclRightLevel.reset (new juce::Slider ("sclRightLevel")); + sclRightLevel.reset (new DXSlider ("sclRightLevel")); addAndMakeVisible (sclRightLevel.get()); sclRightLevel->setTooltip (TRANS ("Keyboard Scale Level Right Depth ")); sclRightLevel->setExplicitFocusOrder (18); @@ -212,7 +212,7 @@ OperatorEditor::OperatorEditor () sclRightLevel->setBounds (241, 115, 34, 34); - sclLvlBrkPt.reset (new juce::Slider ("sclLvlBrkPt")); + sclLvlBrkPt.reset (new DXSlider ("sclLvlBrkPt")); addAndMakeVisible (sclLvlBrkPt.get()); sclLvlBrkPt->setTooltip (TRANS ("Scale Level Breakpoint")); sclLvlBrkPt->setExplicitFocusOrder (17); @@ -223,7 +223,7 @@ OperatorEditor::OperatorEditor () sclLvlBrkPt->setBounds (178, 130, 54, 24); - sclRateScaling.reset (new juce::Slider ("sclRateScaling")); + sclRateScaling.reset (new DXSlider ("sclRateScaling")); addAndMakeVisible (sclRateScaling.get()); sclRateScaling->setTooltip (TRANS ("Keyboard Rate Scaling")); sclRateScaling->setExplicitFocusOrder (20); @@ -234,7 +234,7 @@ OperatorEditor::OperatorEditor () sclRateScaling->setBounds (186, 179, 34, 34); - keyVelSens.reset (new juce::Slider ("keyVelSens")); + keyVelSens.reset (new DXSlider ("keyVelSens")); addAndMakeVisible (keyVelSens.get()); keyVelSens->setExplicitFocusOrder (14); keyVelSens->setRange (0, 7, 1); @@ -244,7 +244,7 @@ OperatorEditor::OperatorEditor () keyVelSens->setBounds (204, 76, 34, 34); - ampModSens.reset (new juce::Slider ("ampModSens")); + ampModSens.reset (new DXSlider ("ampModSens")); addAndMakeVisible (ampModSens.get()); ampModSens->setExplicitFocusOrder (13); ampModSens->setRange (0, 3, 1); diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp index 5045b62b..e02e2e3a 100644 --- a/Source/ParamDialog.cpp +++ b/Source/ParamDialog.cpp @@ -349,6 +349,8 @@ ParamDialog::ParamDialog () //[Constructor] You can add your own custom stuff here.. pitchRangeUp->setEnabled(pitchStep->getValue() == 0); pitchRangeDn->setEnabled(pitchStep->getValue() == 0); + pitchRangeUp->grabKeyboardFocus(); + StringArray input; input.add("None"); From b4c7053edba518683f7e92dde56f600701d93562 Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sun, 29 Sep 2024 02:14:16 -0400 Subject: [PATCH 16/17] Force keyboard focus on parm dialog --- Source/DXComponents.cpp | 2 +- Source/DXLookNFeel.cpp | 21 +++++++++++---------- Source/ParamDialog.cpp | 32 +++++++++++++++++++++----------- Source/ParamDialog.h | 4 +++- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Source/DXComponents.cpp b/Source/DXComponents.cpp index 71f2d411..8fa107a0 100644 --- a/Source/DXComponents.cpp +++ b/Source/DXComponents.cpp @@ -344,7 +344,7 @@ void LcdDisplay::setSystemMsg(String msg) { void LcdDisplay::paint(Graphics &g) { g.setColour (Colours::white); - g.drawText (paramMsg, + g.drawFittedText(paramMsg, 0, 0, 140, 14, Justification::centred, false); } diff --git a/Source/DXLookNFeel.cpp b/Source/DXLookNFeel.cpp index 23475b18..ecf7982d 100644 --- a/Source/DXLookNFeel.cpp +++ b/Source/DXLookNFeel.cpp @@ -276,7 +276,7 @@ Typeface::Ptr DXLookNFeel::getTypefaceForFont(const Font &) { void DXLookNFeel::drawRotarySlider( Graphics &g, int x, int y, int width, int height, float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, Slider &slider ) { if ( imageKnob.isNull() ) { - LookAndFeel_V3::drawRotarySlider(g, x, y, width, height, sliderPosProportional, rotaryStartAngle, rotaryEndAngle, slider); + LookAndFeel_V4::drawRotarySlider(g, x, y, width, height, sliderPosProportional, rotaryStartAngle, rotaryEndAngle, slider); return; } @@ -296,7 +296,7 @@ void DXLookNFeel::drawRotarySlider( Graphics &g, int x, int y, int width, int he void DXLookNFeel::drawToggleButton(Graphics& g, ToggleButton& button, bool isMouseOverButton, bool isButtonDown) { if ( imageSwitch.isNull() ) { - LookAndFeel_V3::drawToggleButton(g, button, isMouseOverButton, isButtonDown); + LookAndFeel_V4::drawToggleButton(g, button, isMouseOverButton, isButtonDown); return; } @@ -305,7 +305,7 @@ void DXLookNFeel::drawToggleButton(Graphics& g, ToggleButton& button, bool isMou if( lb ) { if( imageSwitchLighted.isNull() ) { - LookAndFeel_V3::drawToggleButton(g, button, isMouseOverButton, isButtonDown); + LookAndFeel_V4::drawToggleButton(g, button, isMouseOverButton, isButtonDown); return; } g.drawImage(imageSwitchLighted, 0, 0, 48, 26, 0, button.getToggleState() ? 0 : 26, 48, 26); @@ -317,23 +317,24 @@ void DXLookNFeel::drawToggleButton(Graphics& g, ToggleButton& button, bool isMou void DXLookNFeel::drawButtonBackground(Graphics &g, Button &button, const Colour& backgroundColour, bool isMouseOverButton, bool isButtonDown) { if ( imageButton.isNull() ) { - LookAndFeel_V3::drawButtonBackground(g, button, backgroundColour, isMouseOverButton, isButtonDown); + LookAndFeel_V4::drawButtonBackground(g, button, backgroundColour, isMouseOverButton, isButtonDown); return; } int w = button.getWidth(); - + int l = button.getHeight(); + // dx, dy, dw, dl, sx, sy, sw, sl - g.drawImage(imageButton, 0, 0, 3, 30, 0, isButtonDown ? 30 : 0, 3, 30); - g.drawImage(imageButton, 3, 0, w-6, 30, 3, isButtonDown ? 30 : 0, 44, 30); - g.drawImage(imageButton, w-3, 0, 3, 30, 47, isButtonDown ? 30 : 0, 47, 30); + g.drawImage(imageButton, 0, 0, 3, l, 0, isButtonDown ? 30 : 0, 3, 30); + g.drawImage(imageButton, 3, 0, w-6, l, 3, isButtonDown ? 30 : 0, 44, 30); + g.drawImage(imageButton, w-3, 0, 3, l, 47, isButtonDown ? 30 : 0, 47, 30); } void DXLookNFeel::drawLinearSlider (Graphics& g, int x, int y, int width, int height, float sliderPos, float minSliderPos, float maxSliderPos, const Slider::SliderStyle style, Slider& slider) { if ( imageSlider.isNull() ) { - LookAndFeel_V3::drawLinearSliderThumb(g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); + LookAndFeel_V4::drawLinearSliderThumb(g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); return; } @@ -351,7 +352,7 @@ void DXLookNFeel::positionComboBoxText(ComboBox& box, Label& label) { return; } - LookAndFeel_V3::positionComboBoxText(box, label); + LookAndFeel_V4::positionComboBoxText(box, label); } Colour DXLookNFeel::fillColour = Colour(77,159,151); diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp index e02e2e3a..6ed75f2c 100644 --- a/Source/ParamDialog.cpp +++ b/Source/ParamDialog.cpp @@ -19,6 +19,7 @@ //[Headers] You can add your own extra header files here... #include "Dexed.h" +#include "DXComponents.h" //[/Headers] #include "ParamDialog.h" @@ -33,7 +34,7 @@ ParamDialog::ParamDialog () //[Constructor_pre] You can add your own custom stuff here.. //[/Constructor_pre] - pitchRangeDn.reset (new juce::Slider ("pitchRangeDn")); + pitchRangeDn.reset (new DXSlider ("pitchRangeDn")); addAndMakeVisible (pitchRangeDn.get()); pitchRangeDn->setExplicitFocusOrder (2); pitchRangeDn->setRange (0, 48, 1); @@ -43,7 +44,7 @@ ParamDialog::ParamDialog () pitchRangeDn->setBounds (264, 16, 72, 24); - pitchStep.reset (new juce::Slider ("pitchStep")); + pitchStep.reset (new DXSlider ("pitchStep")); addAndMakeVisible (pitchStep.get()); pitchStep->setExplicitFocusOrder (3); pitchStep->setRange (0, 12, 1); @@ -75,7 +76,7 @@ ParamDialog::ParamDialog () sysexOut->setBounds (104, 280, 224, 24); - sysexChl.reset (new juce::Slider ("sysexChl")); + sysexChl.reset (new DXSlider ("sysexChl")); addAndMakeVisible (sysexChl.get()); sysexChl->setExplicitFocusOrder (9); sysexChl->setRange (1, 16, 1); @@ -107,7 +108,7 @@ ParamDialog::ParamDialog () showKeyboard->setBounds (264, 96, 56, 24); - whlRange.reset (new juce::Slider ("whlRange")); + whlRange.reset (new DXSlider ("whlRange")); addAndMakeVisible (whlRange.get()); whlRange->setExplicitFocusOrder (10); whlRange->setRange (0, 99, 1); @@ -117,7 +118,7 @@ ParamDialog::ParamDialog () whlRange->setBounds (448, 16, 72, 24); - ftRange.reset (new juce::Slider ("ftRange")); + ftRange.reset (new DXSlider ("ftRange")); addAndMakeVisible (ftRange.get()); ftRange->setExplicitFocusOrder (14); ftRange->setRange (0, 99, 1); @@ -127,7 +128,7 @@ ParamDialog::ParamDialog () ftRange->setBounds (448, 56, 72, 24); - brRange.reset (new juce::Slider ("brRange")); + brRange.reset (new DXSlider ("brRange")); addAndMakeVisible (brRange.get()); brRange->setExplicitFocusOrder (18); brRange->setRange (0, 99, 1); @@ -137,7 +138,7 @@ ParamDialog::ParamDialog () brRange->setBounds (448, 96, 72, 24); - atRange.reset (new juce::Slider ("atRange")); + atRange.reset (new DXSlider ("atRange")); addAndMakeVisible (atRange.get()); atRange->setExplicitFocusOrder (22); atRange->setRange (0, 99, 1); @@ -284,7 +285,7 @@ ParamDialog::ParamDialog () transposeScale->setBounds (592, 240, 56, 30); - mpePBRange.reset (new juce::Slider ("mpePBRange")); + mpePBRange.reset (new DXSlider ("mpePBRange")); addAndMakeVisible (mpePBRange.get()); mpePBRange->setExplicitFocusOrder (32); mpePBRange->setRange (0, 96, 1); @@ -312,7 +313,7 @@ ParamDialog::ParamDialog () juce::Image(), 1.000f, juce::Colour (0x00000000)); transposeHelp->setBounds (500, 245, 20, 20); - pitchRangeUp.reset (new juce::Slider ("pitchRangeUp")); + pitchRangeUp.reset (new DXSlider ("pitchRangeUp")); addAndMakeVisible (pitchRangeUp.get()); pitchRangeUp->setExplicitFocusOrder (1); pitchRangeUp->setRange (0, 48, 1); @@ -349,8 +350,6 @@ ParamDialog::ParamDialog () //[Constructor] You can add your own custom stuff here.. pitchRangeUp->setEnabled(pitchStep->getValue() == 0); pitchRangeDn->setEnabled(pitchStep->getValue() == 0); - pitchRangeUp->grabKeyboardFocus(); - StringArray input; input.add("None"); @@ -434,6 +433,9 @@ ParamDialog::ParamDialog () mpeEnabled->setWantsKeyboardFocus(true); transposeHelp->setWantsKeyboardFocus(true); scalingFactor->setWantsKeyboardFocus(true); + + setWantsKeyboardFocus(true); + startTimer(100); //[/Constructor] } @@ -478,6 +480,7 @@ ParamDialog::~ParamDialog() //[Destructor]. You can add your own custom destruction code here.. + stopTimer(); //[/Destructor] } @@ -1203,6 +1206,13 @@ void ParamDialog::setIsStandardTuning( bool b ) resetTuningButton->setEnabled( ! b ); } +// Force the internal component dialog to have keyboard focus. Ugly, but it is +// the only way I have found (including on the JUCE forum). +void ParamDialog::timerCallback() { + stopTimer(); + grabKeyboardFocus(); +} + //[/MiscUserCode] diff --git a/Source/ParamDialog.h b/Source/ParamDialog.h index 8b322305..3fb08d2d 100644 --- a/Source/ParamDialog.h +++ b/Source/ParamDialog.h @@ -40,7 +40,8 @@ class ParamDialog : public Component, public juce::Slider::Listener, public juce::ComboBox::Listener, - public juce::Button::Listener + public juce::Button::Listener, + Timer { public: //============================================================================== @@ -63,6 +64,7 @@ class ParamDialog : public Component, void setGeneralCallback(std::function gc ) { general_callback_ = gc; } void setIsStandardTuning(bool s); + void timerCallback() override; //[/UserMethods] void paint (juce::Graphics& g) override; From 05a62d5fc6f834886c0f25fcce5152ae24987cab Mon Sep 17 00:00:00 2001 From: asb2m10 Date: Sun, 29 Sep 2024 14:39:50 -0400 Subject: [PATCH 17/17] Fix drag and drop for cartmanager --- Documentation/Keybindings.md | 1 + README.md | 6 +++--- Source/CartManager.cpp | 2 +- Source/PluginData.h | 6 +++++- Source/ProgramListBox.h | 9 +++------ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Documentation/Keybindings.md b/Documentation/Keybindings.md index b22d80cd..6543c6f3 100644 --- a/Documentation/Keybindings.md +++ b/Documentation/Keybindings.md @@ -9,3 +9,4 @@ | CTRL+P | Open parameter dialog | | CTRL+C | Copy current context on clipboard as hexa | | CTRL+V | Paste context from clipboard content | +| SHIFT+UP or SHIFT+DOWN | On sliders, it steps 10 % | \ No newline at end of file diff --git a/README.md b/README.md index c88c96de..a36701a7 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,10 @@ Dexed Forks Changelog --------- #### Version 0.9.8 (in development) -* Accessibility implementation -* Cartridge Manager UI refactoring (e.g. display .syx cartridge name) +* UI Refresh +* Accessibility implementation (including keyboard shortcuts) * Mono/Poly parameter is now a plugin parameter -* Fix Logic startup issue +* Fix Apple Logic startup issue #### Version 0.9.7 * [MTS-ESP](https://oddsound.com/index.php) microtuning support diff --git a/Source/CartManager.cpp b/Source/CartManager.cpp index ae79a76f..779026e7 100644 --- a/Source/CartManager.cpp +++ b/Source/CartManager.cpp @@ -424,7 +424,7 @@ void CartManager::programDragged(ProgramListBox *destListBox, int dest, char *pa Cartridge cart; cart.load(file); - memcpy(cart.getRawVoice()+(dest*128), packedPgm, 128); + cart.replaceProgram(dest, packedPgm);; cart.saveVoice(file); browserCart->setCartridge(cart); } diff --git a/Source/PluginData.h b/Source/PluginData.h index fc945e9a..4d0dfa2f 100644 --- a/Source/PluginData.h +++ b/Source/PluginData.h @@ -246,7 +246,11 @@ class Cartridge { memcpy(perfData, other.perfData, SYSEX_SIZE); return *this; } - + + void replaceProgram(int idx, char *src) { + memcpy(getRawVoice() + (idx * 128), src, 128); + } + void unpackProgram(uint8_t *unpackPgm, int idx); void packProgram(uint8_t *src, int idx, String name, char *opSwitch); }; diff --git a/Source/ProgramListBox.h b/Source/ProgramListBox.h index 5336d6ca..274da137 100644 --- a/Source/ProgramListBox.h +++ b/Source/ProgramListBox.h @@ -81,8 +81,7 @@ class ProgramLabel : public Component, public DragAndDropTarget { void paint(Graphics &g) override { if ( inDrag ) { - g.setColour(Colours::black); - g.fillRect(0,0,getWidth(), getHeight()); + g.fillAll(DXLookNFeel::background); return; } @@ -146,9 +145,7 @@ class ProgramLabel : public Component, public DragAndDropTarget { return; if (DragAndDropContainer* const dragContainer = DragAndDropContainer::findParentDragContainerFor(this)) { - Image snapshot (Image::ARGB, getWidth(), getHeight(), true); - Graphics g(snapshot); - paint(g); + ScaledImage snapshot; void *src = pgmListBox->cartContent.getRawVoice() + (idx*128); var description = var(src, 128); dragContainer->startDragging(description, this, snapshot, false); @@ -193,7 +190,7 @@ class ProgramLabel : public Component, public DragAndDropTarget { MemoryBlock* block = dragSourceDetails.description.getBinaryData(); if ( pgmListBox->listener != nullptr ) - pgmListBox->listener->programDragged(pgmListBox, dest->idx, (char *)block->getData()); + pgmListBox->listener->programDragged(pgmListBox, idx, (char *)block->getData()); repaint(); }