mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
GenericAudioProcessorEditor: Add support for grouped parameters
This commit is contained in:
parent
12fd1479a8
commit
cfec0b5356
6 changed files with 146 additions and 90 deletions
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<const __CFString>* stringValues = nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ public:
|
|||
|
||||
if (parametersPtr == nullptr)
|
||||
{
|
||||
numParams = juceParameters.params.size();
|
||||
numParams = (int) juceParameters.size();
|
||||
|
||||
parametersPtr.reset (static_cast<UnityAudioParameterDefinition*> (std::calloc (static_cast<size_t> (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);
|
||||
|
|
|
|||
|
|
@ -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<Vst::ParamID> (i++);
|
||||
|
|
|
|||
|
|
@ -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<LegacyAudioParameter*> (param))
|
||||
if (auto* legacy = dynamic_cast<const LegacyAudioParameter*> (param))
|
||||
return forceLegacyParamIDs ? String (legacy->parameterIndex) : legacy->getParamID();
|
||||
|
||||
if (auto* paramWithID = dynamic_cast<AudioProcessorParameterWithID*> (param))
|
||||
if (auto* paramWithID = dynamic_cast<const AudioProcessorParameterWithID*> (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<LegacyAudioParameter> (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<AudioProcessorParameter*> 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<LegacyAudioParameter> legacy;
|
||||
const AudioProcessorParameterGroup* processorGroup = nullptr;
|
||||
AudioProcessorParameterGroup ownedGroup;
|
||||
Array<AudioProcessorParameter*> params;
|
||||
bool legacyParamIDs = false, usingManagedParameters = false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Component> parameterComp;
|
||||
|
|
@ -468,101 +483,101 @@ private:
|
|||
};
|
||||
|
||||
//==============================================================================
|
||||
class ParametersPanel : public Component
|
||||
struct ParamControlItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
ParametersPanel (AudioProcessor& processor, const Array<AudioProcessorParameter*>& parameters)
|
||||
ParamControlItem (AudioProcessorEditor& editorIn, AudioProcessorParameter& paramIn)
|
||||
: editor (editorIn), param (paramIn) {}
|
||||
|
||||
bool mightContainSubItems() override { return false; }
|
||||
|
||||
std::unique_ptr<Component> createItemComponent() override
|
||||
{
|
||||
for (auto* param : parameters)
|
||||
if (param->isAutomatable())
|
||||
addAndMakeVisible (paramComponents.add (new ParameterDisplayComponent (processor, *param)));
|
||||
return std::make_unique<ParameterDisplayComponent> (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));
|
||||
|
||||
if (auto* inner = node->getGroup())
|
||||
{
|
||||
auto groupItem = std::make_unique<ParameterGroupItem> (editor, *inner);
|
||||
|
||||
if (groupItem->getNumSubItems() != 0)
|
||||
addSubItem (groupItem.release());
|
||||
}
|
||||
}
|
||||
|
||||
setSize (maxWidth, jmax (height, 125));
|
||||
}
|
||||
|
||||
~ParametersPanel() override
|
||||
bool mightContainSubItems() override { return getNumSubItems() > 0; }
|
||||
|
||||
std::unique_ptr<Component> createItemComponent() override
|
||||
{
|
||||
paramComponents.clear();
|
||||
return std::make_unique<Label> (name, name);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto area = getLocalBounds();
|
||||
|
||||
for (auto* comp : paramComponents)
|
||||
comp->setBounds (area.removeFromTop (comp->getHeight()));
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedArray<ParameterDisplayComponent> paramComponents;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParametersPanel)
|
||||
String name;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct GenericAudioProcessorEditor::Pimpl
|
||||
{
|
||||
Pimpl (GenericAudioProcessorEditor& parent) : owner (parent)
|
||||
Pimpl (AudioProcessorEditor& editor)
|
||||
: legacyParameters (editor.processor, false),
|
||||
groupItem (editor, legacyParameters.getGroup())
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
auto* p = parent.getAudioProcessor();
|
||||
jassert (p != nullptr);
|
||||
const auto numIndents = getNumIndents (groupItem);
|
||||
const auto width = 400 + view.getIndentSize() * numIndents;
|
||||
|
||||
legacyParameters.update (*p, false);
|
||||
|
||||
owner.setOpaque (true);
|
||||
|
||||
view.setViewedComponent (new ParametersPanel (*p, legacyParameters.params));
|
||||
owner.addAndMakeVisible (view);
|
||||
|
||||
view.setScrollBarsShown (true, false);
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
view.setSize (width, 400);
|
||||
view.setDefaultOpenness (true);
|
||||
view.setRootItemVisible (false);
|
||||
view.setRootItem (&groupItem);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
static int getNumIndents (const TreeViewItem& item)
|
||||
{
|
||||
view.setViewedComponent (nullptr, false);
|
||||
int maxInner = 0;
|
||||
|
||||
for (auto i = 0; i < item.getNumSubItems(); ++i)
|
||||
maxInner = jmax (maxInner, 1 + getNumIndents (*item.getSubItem (i)));
|
||||
|
||||
return maxInner;
|
||||
}
|
||||
|
||||
void resize (Rectangle<int> size)
|
||||
{
|
||||
view.setBounds (size);
|
||||
auto content = view.getViewedComponent();
|
||||
content->setSize (view.getMaximumVisibleWidth(), content->getHeight());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
GenericAudioProcessorEditor& owner;
|
||||
LegacyAudioParametersWrapper legacyParameters;
|
||||
Viewport view;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
ParameterGroupItem groupItem;
|
||||
TreeView view;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor& p)
|
||||
: AudioProcessorEditor (p), pimpl (new Pimpl (*this))
|
||||
: AudioProcessorEditor (p), pimpl (std::make_unique<Pimpl> (*this))
|
||||
{
|
||||
setSize (pimpl->view.getViewedComponent()->getWidth() + pimpl->view.getVerticalScrollBar().getWidth(),
|
||||
jmin (pimpl->view.getViewedComponent()->getHeight(), 400));
|
||||
auto* viewport = pimpl->view.getViewport();
|
||||
|
||||
setOpaque (true);
|
||||
addAndMakeVisible (pimpl->view);
|
||||
|
||||
setResizable (true, false);
|
||||
setSize (viewport->getViewedComponent()->getWidth() + viewport->getVerticalScrollBar().getWidth(),
|
||||
jlimit (125, 400, viewport->getViewedComponent()->getHeight()));
|
||||
}
|
||||
|
||||
GenericAudioProcessorEditor::~GenericAudioProcessorEditor() {}
|
||||
GenericAudioProcessorEditor::~GenericAudioProcessorEditor() = default;
|
||||
|
||||
void GenericAudioProcessorEditor::paint (Graphics& g)
|
||||
{
|
||||
|
|
@ -571,7 +586,7 @@ void GenericAudioProcessorEditor::paint (Graphics& g)
|
|||
|
||||
void GenericAudioProcessorEditor::resized()
|
||||
{
|
||||
pimpl->resize (getLocalBounds());
|
||||
pimpl->view.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue