diff --git a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index a19f9b0fd0..c7ad0a0d57 100644 --- a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -1548,11 +1548,11 @@ namespace AAXClasses } if (! bypassPartOfRegularParams) - juceParameters.params.add (bypassParameter); + juceParameters.addNonOwning (bypassParameter); int parameterIndex = 0; - for (auto* juceParam : juceParameters.params) + for (auto* juceParam : juceParameters) { auto isBypassParameter = (juceParam == bypassParameter); @@ -2102,7 +2102,7 @@ namespace AAXClasses int meterIdx = 0; - for (auto* param : params.params) + for (auto* param : params) { auto category = param->getCategory(); diff --git a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index 55d597aa3f..37f0486712 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -2058,7 +2058,7 @@ private: } else { - for (auto* param : juceParameters.params) + for (auto* param : juceParameters) { const AudioUnitParameterID auParamID = generateAUParameterID (param); @@ -2075,14 +2075,14 @@ private: #if JUCE_DEBUG // Some hosts can't handle the huge numbers of discrete parameter values created when // using the default number of steps. - for (auto* param : juceParameters.params) + for (auto* param : juceParameters) if (param->isDiscrete()) jassert (param->getNumSteps() != AudioProcessor::getDefaultNumParameterSteps()); #endif parameterValueStringArrays.ensureStorageAllocated (numParams); - for (auto* param : juceParameters.params) + for (auto* param : juceParameters) { OwnedArray* stringValues = nullptr; diff --git a/modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp b/modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp index 7d78eaacf1..08018247c7 100644 --- a/modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/Unity/juce_Unity_Wrapper.cpp @@ -365,7 +365,7 @@ public: if (parametersPtr == nullptr) { - numParams = juceParameters.params.size(); + numParams = (int) juceParameters.size(); parametersPtr.reset (static_cast (std::calloc (static_cast (numParams), sizeof (UnityAudioParameterDefinition)))); @@ -374,7 +374,7 @@ public: for (int i = 0; i < numParams; ++i) { - auto* parameter = juceParameters.params[i]; + auto* parameter = juceParameters.getParamForIndex (i); auto& paramDef = parametersPtr.get()[i]; const auto nameLength = (size_t) numElementsInArray (paramDef.name); diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 28c4842406..f85c9510f0 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -530,13 +530,13 @@ private: // if the bypass parameter is not part of the exported parameters that the plug-in supports // then add it to the end of the list as VST3 requires the bypass parameter to be exported! - bypassIsRegularParameter = juceParameters.params.contains (audioProcessor->getBypassParameter()); + bypassIsRegularParameter = juceParameters.contains (audioProcessor->getBypassParameter()); if (! bypassIsRegularParameter) - juceParameters.params.add (bypassParameter); + juceParameters.addNonOwning (bypassParameter); int i = 0; - for (auto* juceParam : juceParameters.params) + for (auto* juceParam : juceParameters) { bool isBypassParameter = (juceParam == bypassParameter); @@ -568,7 +568,7 @@ private: 0, numPrograms - 1, audioProcessor->getCurrentProgram()); - juceParameters.params.add (ownedProgramParameter.get()); + juceParameters.addNonOwning (ownedProgramParameter.get()); if (forceLegacyParamIDs) programParamID = static_cast (i++); diff --git a/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp b/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp index de2f65a3c8..183dc9cd19 100644 --- a/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp +++ b/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp @@ -98,12 +98,12 @@ public: return -1; } - static String getParamID (AudioProcessorParameter* param, bool forceLegacyParamIDs) noexcept + static String getParamID (const AudioProcessorParameter* param, bool forceLegacyParamIDs) noexcept { - if (auto* legacy = dynamic_cast (param)) + if (auto* legacy = dynamic_cast (param)) return forceLegacyParamIDs ? String (legacy->parameterIndex) : legacy->getParamID(); - if (auto* paramWithID = dynamic_cast (param)) + if (auto* paramWithID = dynamic_cast (param)) { if (! forceLegacyParamIDs) return paramWithID->paramID; @@ -120,6 +120,13 @@ public: class LegacyAudioParametersWrapper { public: + LegacyAudioParametersWrapper() = default; + + LegacyAudioParametersWrapper (AudioProcessor& audioProcessor, bool forceLegacyParamIDs) + { + update (audioProcessor, forceLegacyParamIDs); + } + void update (AudioProcessor& audioProcessor, bool forceLegacyParamIDs) { clear(); @@ -131,15 +138,28 @@ public: for (int i = 0; i < numParameters; ++i) { - AudioProcessorParameter* param = usingManagedParameters ? audioProcessor.getParameters()[i] - : (legacy.add (new LegacyAudioParameter (audioProcessor, i))); + auto* param = [&]() -> AudioProcessorParameter* + { + if (usingManagedParameters) + return audioProcessor.getParameters()[i]; + + auto newParam = std::make_unique (audioProcessor, i); + auto* result = newParam.get(); + ownedGroup.addChild (std::move (newParam)); + + return result; + }(); + params.add (param); } + + processorGroup = usingManagedParameters ? &audioProcessor.getParameterTree() + : nullptr; } void clear() { - legacy.clear(); + ownedGroup = AudioProcessorParameterGroup(); params.clear(); } @@ -159,13 +179,34 @@ public: return String (idx); } + const AudioProcessorParameterGroup& getGroup() const + { + return processorGroup != nullptr ? *processorGroup + : ownedGroup; + } + + void addNonOwning (AudioProcessorParameter* param) + { + params.add (param); + } + + size_t size() const noexcept { return (size_t) params.size(); } + bool isUsingManagedParameters() const noexcept { return usingManagedParameters; } int getNumParameters() const noexcept { return params.size(); } - Array params; + AudioProcessorParameter* const* begin() const { return params.begin(); } + AudioProcessorParameter* const* end() const { return params.end(); } + + bool contains (AudioProcessorParameter* param) const + { + return params.contains (param); + } private: - OwnedArray legacy; + const AudioProcessorParameterGroup* processorGroup = nullptr; + AudioProcessorParameterGroup ownedGroup; + Array params; bool legacyParamIDs = false, usingManagedParameters = false; }; diff --git a/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp b/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp index 4d49756040..bc2c6dbfa9 100644 --- a/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp +++ b/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp @@ -409,23 +409,23 @@ private: class ParameterDisplayComponent : public Component { public: - ParameterDisplayComponent (AudioProcessor& processor, AudioProcessorParameter& param) - : parameter (param) + ParameterDisplayComponent (AudioProcessorEditor& editorIn, AudioProcessorParameter& param) + : editor (editorIn), parameter (param) { parameterName.setText (parameter.getName (128), dontSendNotification); parameterName.setJustificationType (Justification::centredRight); + parameterName.setInterceptsMouseClicks (false, false); addAndMakeVisible (parameterName); parameterLabel.setText (parameter.getLabel(), dontSendNotification); + parameterLabel.setInterceptsMouseClicks (false, false); addAndMakeVisible (parameterLabel); - addAndMakeVisible (*(parameterComp = createParameterComp (processor))); + addAndMakeVisible (*(parameterComp = createParameterComp (editor.processor))); setSize (400, 40); } - void paint (Graphics&) override {} - void resized() override { auto area = getLocalBounds(); @@ -435,7 +435,22 @@ public: parameterComp->setBounds (area); } + void mouseDown (const MouseEvent& e) override + { + if (e.mods.isRightButtonDown()) + if (auto* context = editor.getHostContext()) + if (auto menu = context->getContextMenuForParameterIndex (¶meter)) + menu->getEquivalentPopupMenu().showMenuAsync (getMenuOptions()); + } + private: + PopupMenu::Options getMenuOptions() + { + return PopupMenu::Options().withTargetComponent (this) + .withTargetScreenArea ({ Desktop::getMousePosition(), Desktop::getMousePosition() }); + } + + AudioProcessorEditor& editor; AudioProcessorParameter& parameter; Label parameterName, parameterLabel; std::unique_ptr parameterComp; @@ -468,101 +483,101 @@ private: }; //============================================================================== -class ParametersPanel : public Component +struct ParamControlItem : public TreeViewItem { -public: - ParametersPanel (AudioProcessor& processor, const Array& parameters) + ParamControlItem (AudioProcessorEditor& editorIn, AudioProcessorParameter& paramIn) + : editor (editorIn), param (paramIn) {} + + bool mightContainSubItems() override { return false; } + + std::unique_ptr createItemComponent() override { - for (auto* param : parameters) - if (param->isAutomatable()) - addAndMakeVisible (paramComponents.add (new ParameterDisplayComponent (processor, *param))); + return std::make_unique (editor, param); + } - int maxWidth = 400; - int height = 0; + int getItemHeight() const override { return 40; } - for (auto& comp : paramComponents) + AudioProcessorEditor& editor; + AudioProcessorParameter& param; +}; + +struct ParameterGroupItem : public TreeViewItem +{ + ParameterGroupItem (AudioProcessorEditor& editor, const AudioProcessorParameterGroup& group) + : name (group.getName()) + { + for (auto* node : group) { - maxWidth = jmax (maxWidth, comp->getWidth()); - height += comp->getHeight(); - } + if (auto* param = node->getParameter()) + if (param->isAutomatable()) + addSubItem (new ParamControlItem (editor, *param)); - setSize (maxWidth, jmax (height, 125)); - } + if (auto* inner = node->getGroup()) + { + auto groupItem = std::make_unique (editor, *inner); - ~ParametersPanel() override - { - paramComponents.clear(); + if (groupItem->getNumSubItems() != 0) + addSubItem (groupItem.release()); + } + } } - void paint (Graphics& g) override - { - g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); - } + bool mightContainSubItems() override { return getNumSubItems() > 0; } - void resized() override + std::unique_ptr createItemComponent() override { - auto area = getLocalBounds(); - - for (auto* comp : paramComponents) - comp->setBounds (area.removeFromTop (comp->getHeight())); + return std::make_unique