diff --git a/src/common/ModulationSource.h b/src/common/ModulationSource.h index 1fb61f996d9..35d7626d1e6 100644 --- a/src/common/ModulationSource.h +++ b/src/common/ModulationSource.h @@ -128,7 +128,7 @@ const char modsource_names_button[n_modsources][32] = { "Macro 6", "Macro 7", "Macro 8", "Amp EG", "Filter EG", "LFO 1", "LFO 2", "LFO 3", "LFO 4", "LFO 5", "LFO 6", "S-LFO 1", "S-LFO 2", "S-LFO 3", "S-LFO 4", "S-LFO 5", "S-LFO 6", "MPE Timbre", - "Rel Velocity", "Random", "Random Uni", "Alternate", "Alternate Uni", "Breath", + "Rel Velocity", "Random", "RandUni", "Alternate", "Alternate Uni", "Breath", "Expression", "Sustain", "Lowest Key", "Highest Key", "Latest Key", }; @@ -185,50 +185,6 @@ const char modsource_names_tag[n_modsources][32] = { "breath", "expr", "sustain", "lowest_key", "highest_key", "latest_key", }; -const int modsource_grid_xy[n_modsources][2] = { - {0, 0}, // Velocity - {0, 3}, // Keytrack - {6, 7}, // Poly AT - {2, 3}, // Channel - {3, 3}, // AT - {4, 3}, // Pitch Bend - {5, 3}, // Modwheel - {0, 0}, // Macro 1 - {1, 0}, // Macro 2 - {2, 0}, // Macro 3 - {3, 0}, // Macro 4 - {4, 0}, // Macro 5 - {5, 0}, // Macro 6 - {6, 0}, // Macro 7 - {7, 0}, // Macro 8 - {7, 5}, // AEG - {6, 5}, // FEG - {0, 5}, // LFO 1 - {1, 5}, // LFO 2 - {2, 5}, // LFO 3 - {3, 5}, // LFO 4 - {4, 5}, // LFO 5 - {5, 5}, // LFO 6 - {0, 7}, // S-LFO 1 - {1, 7}, // S-LFO 2 - {2, 7}, // S-LFO 3 - {3, 7}, // S-LFO 4 - {4, 7}, // S-LFO 5 - {5, 7}, // S-LFO 6 - {9, 3}, // Timbre - {1, 3}, // Release Velocity - {8, 5}, // Random Bi - {8, 5}, // Random Uni - {9, 5}, // Alternate Bi - {9, 5}, // Alternate Uni - {6, 3}, // Breath - {7, 3}, // Expression - {8, 3}, // Sustain - {7, 7}, // Lowest Key - {8, 7}, // Highest Key - {9, 7}, // Latest Key -}; - inline bool isScenelevel(modsources ms) { return (((ms <= ms_ctrl8) || ((ms >= ms_slfo1) && (ms <= ms_slfo6))) && diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index d2cd05a691b..53be5be0d29 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -2765,7 +2765,7 @@ void SurgeSynthesizer::clear_osc_modulation(int scene, int entry) bool SurgeSynthesizer::supportsIndexedModulator(int scene, modsources modsource) const { - if (modsource >= ms_lfo1 && modsource <= ms_lfo6) + if (modsource >= ms_lfo1 && modsource <= ms_slfo6) { auto lf = &(storage.getPatch().scene[scene].lfo[modsource - ms_lfo1]); return lf->shape.val.i == lt_formula; diff --git a/src/gui/ModulationGridConfiguration.h b/src/gui/ModulationGridConfiguration.h new file mode 100644 index 00000000000..0c70a27ba1b --- /dev/null +++ b/src/gui/ModulationGridConfiguration.h @@ -0,0 +1,117 @@ +/* +** Surge Synthesizer is Free and Open Source Software +** +** Surge is made available under the Gnu General Public License, v3.0 +** https://www.gnu.org/licenses/gpl-3.0.en.html +** +** Copyright 2004-2021 by various individuals as described by the Git transaction log +** +** All source at: https://github.com/surge-synthesizer/surge.git +** +** Surge was a commercial product from 2004-2018, with Copyright and ownership +** in that period held by Claes Johanson at Vember Audio. Claes made Surge +** open source in September 2018. +*/ +#ifndef SURGE_XT_MODULATIONGRIDCONFIGURATION_H +#define SURGE_XT_MODULATIONGRIDCONFIGURATION_H + +#include "ModulationSource.h" + +#include + +namespace Surge +{ +namespace GUI +{ +struct ModulationGrid : juce::DeletedAtShutdown +{ + ModulationGrid() + { + add(ms_original, 0, 0); + + add(ms_velocity, 0, 3); + add(ms_keytrack, 6, 7); + add(ms_polyaftertouch, 2, 3); + add(ms_aftertouch, 3, 3); + add(ms_pitchbend, 4, 3); + add(ms_modwheel, 5, 3); + + for (int i = ms_ctrl1; i <= ms_ctrl8; ++i) + { + add((modsources)i, (int)i - ms_ctrl1, 0); + } + + add(ms_ampeg, 7, 5); + add(ms_filtereg, 6, 5); + + for (int i = ms_lfo1; i <= ms_lfo6; ++i) + { + add((modsources)i, (int)i - ms_lfo1, 5); + } + for (int i = ms_slfo1; i <= ms_slfo6; ++i) + { + add((modsources)i, (int)i - ms_slfo1, 7); + } + + add(ms_timbre, 9, 3); + add(ms_releasevelocity, 1, 3); + add(ms_random_bipolar, 8, 5, {ms_random_unipolar}); + add(ms_alternate_bipolar, 9, 5, {ms_alternate_unipolar}); + add(ms_breath, 6, 3); + add(ms_sustain, 8, 3); + add(ms_expression, 7, 3); + add(ms_lowest_key, 7, 7); + add(ms_highest_key, 8, 7); + add(ms_latest_key, 9, 7); + } + + void add(modsources ms, int x, int y) { data[ms] = Entry{x, y, ms}; } + void add(modsources ms, int x, int y, const std::vector &alternates) + { + data[ms] = Entry{x, y, ms, alternates}; + for (auto a : alternates) + { + data[a] = Entry{a, ms}; + } + } + struct Entry + { + Entry() {} + Entry(int x, int y, modsources ms) : x(x), y(y), ms(ms) {} + Entry(int x, int y, modsources ms, const std::vector &alternates) + : x(x), y(y), ms(ms), alternates(alternates) + { + } + Entry(modsources sub, modsources primary) + : ms(sub), associatedPrimary(primary), isPrimary(false) + { + } + + modsources ms{ms_original}; + modsources associatedPrimary{ms_original}; + int x{-1}, y{-1}; + bool isPrimary{true}; + std::vector alternates; + }; + + std::unordered_map data; + + const Entry &get(modsources ms) const + { + jassert(data.find(ms) != data.end()); + return data.at(ms); + } + + static ModulationGrid *grid; + static inline const ModulationGrid *getModulationGrid() + { + // The juce::DeletedAtShutdown makes sure this gets cleared up + if (!grid) + grid = new ModulationGrid(); + return grid; + } +}; +} // namespace GUI +} // namespace Surge + +#endif // SURGE_XT_MODULATIONGRIDCONFIGURATION_H diff --git a/src/gui/SurgeGUIEditor.cpp b/src/gui/SurgeGUIEditor.cpp index 4ff7db6255d..877cb13dfc2 100644 --- a/src/gui/SurgeGUIEditor.cpp +++ b/src/gui/SurgeGUIEditor.cpp @@ -61,6 +61,8 @@ #include "widgets/WaveShaperSelector.h" #include "widgets/XMLConfiguredMenus.h" +#include "ModulationGridConfiguration.h" + #include #include #include @@ -81,9 +83,12 @@ using namespace std; using namespace Surge::ParamConfig; int SurgeGUIEditor::start_paramtag_value = start_paramtags; +Surge::GUI::ModulationGrid *Surge::GUI::ModulationGrid::grid = nullptr; SurgeGUIEditor::SurgeGUIEditor(SurgeSynthEditor *jEd, SurgeSynthesizer *synth) { + jassert(Surge::GUI::ModulationGrid::getModulationGrid()); + assert(n_paramslots >= n_total_params); synth->storage.addErrorListener(this); synth->storage.okCancelProvider = [this](const std::string &msg, const std::string &title, @@ -122,9 +127,6 @@ SurgeGUIEditor::SurgeGUIEditor(SurgeSynthEditor *jEd, SurgeSynthesizer *synth) minimumZoom = 100; // See github issue #628 #endif - for (int i = 0; i < n_modsources; ++i) - modsource_is_alternate[i] = false; - currentSkin = Surge::GUI::SkinDB::get()->defaultSkin(&(this->synth->storage)); // init the size of the plugin @@ -738,8 +740,6 @@ void SurgeGUIEditor::refresh_mod() return; modsources thisms = modsource; - if (cms->getHasAlternate() && cms->getUseAlternate()) - thisms = cms->getAlternate(); synth->storage.modRoutingMutex.lock(); for (int i = 0; i < n_paramslots; i++) @@ -796,12 +796,7 @@ void SurgeGUIEditor::refresh_mod() // this could change if I cleared the last one gui_modsrc[i]->setUsed(synth->isModsourceUsed((modsources)i)); gui_modsrc[i]->setState(state); - - if (i < ms_ctrl1 || i > ms_ctrl8) - { - auto mn = modulatorName(i, true); - gui_modsrc[i]->setLabel(mn.c_str()); - } + setupAlternates((modsources)i); gui_modsrc[i]->repaint(); } } @@ -898,8 +893,9 @@ bool SurgeGUIEditor::isControlVisible(ControlGroup controlGroup, int controlGrou juce::Rectangle SurgeGUIEditor::positionForModulationGrid(modsources entry) { bool isMacro = isCustomController(entry); - int gridX = modsource_grid_xy[entry][0]; - int gridY = modsource_grid_xy[entry][1]; + + int gridX = Surge::GUI::ModulationGrid::getModulationGrid()->get(entry).x; + int gridY = Surge::GUI::ModulationGrid::getModulationGrid()->get(entry).y; int width = isMacro ? 93 : 74; // to ensure the same gap between the modbuttons, @@ -991,10 +987,11 @@ void SurgeGUIEditor::openOrRecreateEditor() */ for (int k = 1; k < n_modsources; k++) { - if ((k != ms_random_unipolar) && (k != ms_alternate_unipolar)) - { - modsources ms = (modsources)k; + modsources ms = (modsources)k; + auto e = Surge::GUI::ModulationGrid::getModulationGrid()->get(ms); + if (e.isPrimary) + { auto r = positionForModulationGrid(ms); int state = 0; @@ -1008,7 +1005,6 @@ void SurgeGUIEditor::openOrRecreateEditor() { gui_modsrc[ms] = std::make_unique(); } - gui_modsrc[ms]->setModSource(ms); gui_modsrc[ms]->setBounds(r); gui_modsrc[ms]->setTag(tag_mod_source0 + ms); gui_modsrc[ms]->addListener(this); @@ -1017,10 +1013,12 @@ void SurgeGUIEditor::openOrRecreateEditor() gui_modsrc[ms]->update_rt_vals(false, 0, synth->isModsourceUsed(ms)); + setupAlternates(ms); + if ((ms >= ms_ctrl1) && (ms <= ms_ctrl8)) { // std::cout << synth->learn_custom << std::endl; - gui_modsrc[ms]->setLabel( + gui_modsrc[ms]->setCurrentModLabel( synth->storage.getPatch().CustomControllerLabel[ms - ms_ctrl1]); gui_modsrc[ms]->setIsMeta(true); gui_modsrc[ms]->setBipolar( @@ -1030,24 +1028,6 @@ void SurgeGUIEditor::openOrRecreateEditor() .modsources[ms]) ->get_target01()); } - else - { - gui_modsrc[ms]->setLabel(modulatorName(ms, true).c_str()); - - if (ms == ms_random_bipolar) - { - gui_modsrc[ms]->setAlternate(ms_random_unipolar, - modsource_names_button[ms_random_unipolar]); - gui_modsrc[ms]->setUseAlternate(modsource_is_alternate[ms]); - } - - if (ms == ms_alternate_bipolar) - { - gui_modsrc[ms]->setAlternate(ms_alternate_unipolar, - modsource_names_button[ms_alternate_unipolar]); - gui_modsrc[ms]->setUseAlternate(modsource_is_alternate[ms]); - } - } frame->getModButtonLayer()->addAndMakeVisible(*gui_modsrc[ms]); if (ms >= ms_ctrl1 && ms <= ms_ctrl8 && synth->learn_custom == ms - ms_ctrl1) @@ -4628,8 +4608,6 @@ void SurgeGUIEditor::swapFX(int source, int target, SurgeSynthesizer::FXReorderM void SurgeGUIEditor::lfoShapeChanged(int prior, int curr) { - std::cout << "lfoShapeChanged" << prior << " " << curr << std::endl; - if (prior != curr || prior == lt_mseg || curr == lt_mseg || prior == lt_formula || curr == lt_formula) { @@ -4651,7 +4629,6 @@ void SurgeGUIEditor::lfoShapeChanged(int prior, int curr) closeFormulaEditorDialog(); hadExtendedEditor = true; } - std::cout << _D(prior) << _D(curr) << _D(hadExtendedEditor) << std::endl; if (hadExtendedEditor) { @@ -4670,6 +4647,7 @@ void SurgeGUIEditor::lfoShapeChanged(int prior, int curr) lfoNameLabel->setText(modname.c_str()); lfoNameLabel->repaint(); + setupAlternates(modsource_editor[current_scene]); // And now we have dynamic labels really anything frame->repaint(); } @@ -4942,25 +4920,64 @@ void SurgeGUIEditor::setAccessibilityInformationByTitleAndAction(juce::Component #endif } -std::string SurgeGUIEditor::modulatorIndexExtension(int scene, int ms, int index) +std::string SurgeGUIEditor::modulatorIndexExtension(int scene, int ms, int index, bool shortV) { if (synth->supportsIndexedModulator(scene, (modsources)ms)) { if (ms == ms_random_bipolar) { if (index == 0) - return " (Uniform)"; + return shortV ? "" : " (Uniform)"; if (index == 1) - return " (Normal)"; + return shortV ? " N" : " (Normal)"; } if (ms == ms_random_unipolar) { if (index == 0) - return " (Uniform)"; + return shortV ? "" : " (Uniform)"; if (index == 1) - return " (Half Normal)"; + return shortV ? " hN" : " (Half Normal)"; } - return std::string(" Out ") + std::to_string(index + 1); + if (shortV) + return "." + std::to_string(index + 1); + else + return std::string(" Out ") + std::to_string(index + 1); } return ""; +} + +void SurgeGUIEditor::setupAlternates(modsources ms) +{ + jassert(gui_modsrc[ms]); + if (!gui_modsrc[ms]) + return; + + auto e = Surge::GUI::ModulationGrid::getModulationGrid()->get(ms); + Surge::Widgets::ModulationSourceButton::modlist_t indexedAlternates; + std::vector traverse; + traverse.push_back(ms); + for (auto a : e.alternates) + traverse.push_back(a); + + for (auto a : traverse) + { + auto baseLabel = modulatorName(a, true); + auto baseLongName = modulatorName(a, false); + + int idxc = 1; + if (synth->supportsIndexedModulator(current_scene, a)) + idxc = synth->getMaxModulationIndex(current_scene, a); + for (int q = 0; q < idxc; ++q) + { + auto tl = baseLabel; + auto ll = baseLongName; + if (idxc > 1) + { + tl += modulatorIndexExtension(current_scene, a, q, true); + ll += modulatorIndexExtension(current_scene, a, q, false); + } + indexedAlternates.emplace_back(a, q, tl, ll); + } + } + gui_modsrc[ms]->setModList(indexedAlternates); } \ No newline at end of file diff --git a/src/gui/SurgeGUIEditor.h b/src/gui/SurgeGUIEditor.h index e3bcbabb98f..491a8be118c 100644 --- a/src/gui/SurgeGUIEditor.h +++ b/src/gui/SurgeGUIEditor.h @@ -447,9 +447,6 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener, public: std::shared_ptr bitmapStore = nullptr; - private: - bool modsource_is_alternate[n_modsources]; - private: std::array, n_fx_slots + 1> vu; std::unique_ptr patchSelector; @@ -459,6 +456,8 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener, /* Infowindow members and functions */ std::unique_ptr paramInfowindow; + void setupAlternates(modsources ms); + public: void showInfowindow(int ptag, juce::Rectangle relativeTo, bool isModulated); void showInfowindowSelfDismiss(int ptag, juce::Rectangle relativeTo, bool isModulated); @@ -544,7 +543,7 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener, public: std::string modulatorName(int ms, bool forButton); - std::string modulatorIndexExtension(int scene, int ms, int index); + std::string modulatorIndexExtension(int scene, int ms, int index, bool shortV = false); private: Parameter *typeinEditTarget = nullptr; diff --git a/src/gui/SurgeGUIEditorValueCallbacks.cpp b/src/gui/SurgeGUIEditorValueCallbacks.cpp index beda1107ed3..bd5fbe7ddda 100644 --- a/src/gui/SurgeGUIEditorValueCallbacks.cpp +++ b/src/gui/SurgeGUIEditorValueCallbacks.cpp @@ -256,11 +256,11 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c if ((tag >= tag_mod_source0) && (tag < tag_mod_source_end)) { - modsources modsource = (modsources)(tag - tag_mod_source0); + auto *cms = dynamic_cast(control); + modsources modsource = cms->getCurrentModSource(); if (button.isRightButtonDown() || button.isPopupMenu()) { - auto *cms = dynamic_cast(control); juce::PopupMenu contextMenu; std::string hu; @@ -282,6 +282,7 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c hu = helpURLForSpecial("other-modbutton"); } +#if USE_OLD_ALTERNATES if (cms->getHasAlternate()) { int idOn = modsource; @@ -314,6 +315,10 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c modsource_is_alternate[modsource] = cms->getUseAlternate(); this->refresh_mod(); }); +#else + if (false) + { +#endif } else { @@ -351,10 +356,12 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c std::vector possibleSources; possibleSources.push_back(modsource); +#if FIXME_ALTERNATES if (cms->getHasAlternate()) { possibleSources.push_back((modsources)(cms->getAlternate())); } +#endif for (auto thisms : possibleSources) { @@ -514,7 +521,7 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c if (msb) { - msb->setLabel( + msb->setCurrentModLabel( synth->storage.getPatch().CustomControllerLabel[ccid]); } @@ -570,7 +577,7 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c auto msb = dynamic_cast(control); if (msb) - msb->setLabel( + msb->setCurrentModLabel( synth->storage.getPatch().CustomControllerLabel[ccid]); } if (control->asJuceComponent()) @@ -760,7 +767,7 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c if (msb) { - msb->setLabel( + msb->setCurrentModLabel( synth->storage.getPatch().CustomControllerLabel[ccid]); } @@ -2026,8 +2033,10 @@ int32_t SurgeGUIEditor::controlModifierClicked(Surge::GUI::IComponentTagValue *c { auto *cms = gui_modsrc[modsource].get(); modsources thisms = modsource; +#if FIXME_ALTERNATES if (cms->getHasAlternate() && cms->getUseAlternate()) thisms = cms->getAlternate(); +#endif synth->clearModulation(ptag, thisms, modsource_index); auto ctrms = dynamic_cast(control); @@ -2239,7 +2248,8 @@ void SurgeGUIEditor::valueChanged(Surge::GUI::IComponentTagValue *control) else { int state = cms->getState(); - modsources newsource = (modsources)(tag - tag_mod_source0); + modsources newsource = cms->getCurrentModSource(); + int newindex = cms->getCurrentModIndex(); if (cms->getMouseMode() == Surge::Widgets::ModulationSourceButton::CLICK) { @@ -2247,6 +2257,7 @@ void SurgeGUIEditor::valueChanged(Surge::GUI::IComponentTagValue *control) { case 0: modsource = newsource; + modsource_index = newindex; if (mod_editor) mod_editor = true; else @@ -2256,16 +2267,24 @@ void SurgeGUIEditor::valueChanged(Surge::GUI::IComponentTagValue *control) break; case 1: modsource = newsource; + modsource_index = newindex; mod_editor = true; refresh_mod(); break; case 2: modsource = newsource; + modsource_index = newindex; mod_editor = false; refresh_mod(); break; }; } + else if (cms->getMouseMode() == Surge::Widgets::ModulationSourceButton::HAMBURGER) + { + modsource = newsource; + modsource_index = newindex; + refresh_mod(); + } if (isLFO(newsource)) { @@ -2552,8 +2571,10 @@ void SurgeGUIEditor::valueChanged(Surge::GUI::IComponentTagValue *control) if (gui_modsrc[modsource]) { auto cms = gui_modsrc[modsource].get(); +#if FIXME_ALTERNATES if (cms->getHasAlternate() && cms->getUseAlternate()) thisms = cms->getAlternate(); +#endif } bool quantize_mod = juce::ModifierKeys::currentModifiers.isCtrlDown(); float mv = mci->getModValue(); @@ -2588,7 +2609,7 @@ void SurgeGUIEditor::valueChanged(Surge::GUI::IComponentTagValue *control) strxcpy(lbl, p->get_name(), CUSTOM_CONTROLLER_LABEL_SIZE - 1); synth->storage.getPatch() .CustomControllerLabel[ccid][CUSTOM_CONTROLLER_LABEL_SIZE - 1] = 0; - gui_modsrc[modsource]->setLabel(lbl); + gui_modsrc[modsource]->setCurrentModLabel(lbl); gui_modsrc[modsource]->repaint(); } } diff --git a/src/gui/widgets/ModulationSourceButton.cpp b/src/gui/widgets/ModulationSourceButton.cpp index 1ed5b0ee148..16953dc13c3 100644 --- a/src/gui/widgets/ModulationSourceButton.cpp +++ b/src/gui/widgets/ModulationSourceButton.cpp @@ -142,14 +142,7 @@ void ModulationSourceButton::paint(juce::Graphics &g) g.setFont(Surge::GUI::getFontManager()->displayFont); // modbutton name - if (hasAlternate && useAlternate) - { - g.drawText(alternateLabel, getLocalBounds(), juce::Justification::centred); - } - else - { - g.drawText(label, getLocalBounds(), juce::Justification::centred); - } + g.drawText(getCurrentModLabel(), getLocalBounds(), juce::Justification::centred); // modbutton frame g.setColour(FrameCol); @@ -161,7 +154,7 @@ void ModulationSourceButton::paint(juce::Graphics &g) auto topRect = getLocalBounds().withHeight(splitHeight); g.setColour(FontCol); g.setFont(Surge::GUI::getFontManager()->displayFont); - g.drawText(label, topRect, juce::Justification::centred); + g.drawText(getCurrentModLabel(), topRect, juce::Justification::centred); // macro slider area auto bottomRect = fillRect.withTrimmedTop(splitHeight - 2); @@ -218,7 +211,7 @@ void ModulationSourceButton::paint(juce::Graphics &g) g.drawRect(getLocalBounds(), 1); } - if (modSource >= ms_lfo1 && modSource <= ms_slfo6) + if (isLFO()) { auto arrSze = getLocalBounds().withLeft(getLocalBounds().getRight() - 14).withHeight(16); juce::Graphics::ScopedSaveState gs(g); @@ -227,6 +220,22 @@ void ModulationSourceButton::paint(juce::Graphics &g) arrow->drawAt(g, arrSze.getX(), arrSze.getY() + dy, 1.0); } + + if (needsHamburger()) + { + g.setColour(FrameCol); + float ht = 1.5; + int nburg = 4; + float pxspace = 1.f * (hamburgerHome.getHeight() - ht) / (nburg - 1); + + for (int i = 0; i < nburg; ++i) + { + auto r = + juce::Rectangle(hamburgerHome.getX(), hamburgerHome.getY() + i * pxspace, + hamburgerHome.getWidth(), ht); + g.fillRoundedRectangle(r, 1); + } + } } void ModulationSourceButton::mouseDown(const juce::MouseEvent &event) @@ -234,6 +243,25 @@ void ModulationSourceButton::mouseDown(const juce::MouseEvent &event) mouseMode = CLICK; everDragged = false; + if (needsHamburger() && hamburgerHome.contains(event.position.toInt())) + { + auto menu = juce::PopupMenu(); + int idx{0}; + for (auto e : modlist) + { + menu.addItem(std::get<3>(e), [this, idx]() { + this->modlistIndex = idx; + mouseMode = HAMBURGER; + notifyValueChanged(); + mouseMode = NONE; + repaint(); + }); + idx++; + } + mouseMode = HAMBURGER; + menu.showMenuAsync(juce::PopupMenu::Options()); + return; + } if (event.mods.isPopupMenu()) { mouseMode = NONE; @@ -259,7 +287,7 @@ void ModulationSourceButton::mouseDown(const juce::MouseEvent &event) } } - if (modSource >= ms_lfo1 && modSource <= ms_slfo6) + if (isLFO()) { auto arrSze = getLocalBounds().withLeft(getLocalBounds().getRight() - 14).withHeight(16); @@ -415,5 +443,10 @@ void ModulationSourceButton::mouseWheelMove(const juce::MouseEvent &event, repaint(); } } + +void ModulationSourceButton::resized() +{ + hamburgerHome = getLocalBounds().withWidth(11).reduced(2, 2); +} } // namespace Widgets } // namespace Surge \ No newline at end of file diff --git a/src/gui/widgets/ModulationSourceButton.h b/src/gui/widgets/ModulationSourceButton.h index 094b127aed4..66f7b1140ce 100644 --- a/src/gui/widgets/ModulationSourceButton.h +++ b/src/gui/widgets/ModulationSourceButton.h @@ -48,61 +48,58 @@ struct ModulationSourceButton : public juce::Component, SurgeStorage *storage{nullptr}; void setStorage(SurgeStorage *s) { storage = s; } - std::string label; - void setLabel(const std::string &s) - { - label = s; - setAccessibleLabel(label); - } - void setAccessibleLabel(const std::string &s) { #if SURGE_JUCE_ACCESSIBLE #if MAC - setDescription(std::string("Modulator ") + label); - setTitle(label); + setDescription(std::string("Modulator ") + s); + setTitle(s); #else setDescription("Modulator"); - setTitle(label); + setTitle(s); #endif #endif } - modsources modSource; - void setModSource(modsources ms) { modSource = ms; } - bool isMeta{false}, isBipolar{false}; - void setIsMeta(bool b) { isMeta = b; } - void setBipolar(bool b) { isBipolar = b; } + modsources getCurrentModSource() const { return std::get<0>(modlist[modlistIndex]); } + int getCurrentModIndex() const { return std::get<1>(modlist[modlistIndex]); } + std::string getCurrentModLabel() const { return std::get<2>(modlist[modlistIndex]); } - bool hasAlternate{false}, useAlternate{false}; - modsources alternateSource{ms_original}; - std::string alternateLabel; + void setCurrentModLabel(const std::string &s) + { + jassert(modlist.size() == 1 && modlistIndex == 0); + std::get<2>(modlist[modlistIndex]) = s; + repaint(); + } - void setAlternate(modsources alt, const std::string &s) + typedef std::vector> modlist_t; + modlist_t modlist; + int modlistIndex{0}; + void setModList(const modlist_t &m) { - alternateSource = alt; - alternateLabel = s; - hasAlternate = true; + modlistIndex = limit_range(modlistIndex, 0, (int)(m.size() - 1)); + modlist = m; + setAccessibleLabel(getCurrentModLabel()); } + bool isMeta{false}, isBipolar{false}; + void setIsMeta(bool b) { isMeta = b; } + void setBipolar(bool b) { isBipolar = b; } - modsources getAlternate() { return alternateSource; } - bool getHasAlternate() { return hasAlternate; } - void setUseAlternate(bool b) + juce::Rectangle hamburgerHome; + bool needsHamburger() const { return modlist.size() > 1; } + + bool isLFO() const { - useAlternate = b; - if (useAlternate) + for (auto m : modlist) { - setAccessibleLabel(alternateLabel); - } - else - { - setAccessibleLabel(label); + if (std::get<0>(m) >= ms_lfo1 && std::get<0>(m) <= ms_slfo6) + { + return true; + } } + return false; } - bool getUseAlternate() const { return useAlternate; } - bool isUsed{false}; - void setUsed(bool b) { isUsed = b; } int state{0}; @@ -151,7 +148,8 @@ struct ModulationSourceButton : public juce::Component, CLICK, CLICK_ARROW, DRAG_VALUE, - DRAG_COMPONENT_HAPPEN + DRAG_COMPONENT_HAPPEN, + HAMBURGER } mouseMode{NONE}; MouseState getMouseMode() const { return mouseMode; } juce::ComponentDragger componentDragger; @@ -164,6 +162,8 @@ struct ModulationSourceButton : public juce::Component, void mouseWheelMove(const juce::MouseEvent &event, const juce::MouseWheelDetails &wheel) override; + void resized() override; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ModulationSourceButton); }; } // namespace Widgets