#include "EffectsListComponent.h" #include "SvgButton.h" #include "../PluginEditor.h" #include "../LookAndFeel.h" EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect) : DraggableListBoxItem(lb, data, rn), effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) { auto parameters = effect.parameters; for (int i = 0; i < parameters.size(); i++) { std::shared_ptr effectComponent = std::make_shared(audioProcessor, effect, i); selected.setToggleState(effect.enabled == nullptr || effect.enabled->getValue(), juce::dontSendNotification); // using weak_ptr to avoid circular reference and memory leak std::weak_ptr weakEffectComponent = effectComponent; effectComponent->slider.setValue(parameters[i]->getValueUnnormalised(), juce::dontSendNotification); effectComponent->slider.onValueChange = [this, i, weakEffectComponent] { if (auto effectComponent = weakEffectComponent.lock()) { this->effect.setValue(i, effectComponent->slider.getValue()); } }; list.setEnabled(selected.getToggleState()); selected.onClick = [this, weakEffectComponent] { if (auto effectComponent = weakEffectComponent.lock()) { auto data = (AudioEffectListBoxItemData&)modelData; juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock); data.setSelected(rowNum, selected.getToggleState()); list.setEnabled(selected.getToggleState()); } repaint(); }; auto component = createComponent(parameters[i]); if (component != nullptr) { effectComponent->setComponent(component); } listModel.addComponent(effectComponent); } list.setColour(effectComponentBackgroundColourId, juce::Colours::transparentBlack.withAlpha(0.2f)); list.setModel(&listModel); list.setRowHeight(ROW_HEIGHT); list.updateContent(); addAndMakeVisible(list); addAndMakeVisible(selected); } EffectsListComponent::~EffectsListComponent() { list.setLookAndFeel(nullptr); } void EffectsListComponent::paint(juce::Graphics& g) { auto bounds = getLocalBounds().removeFromLeft(LEFT_BAR_WIDTH); g.setColour(findColour(effectComponentHandleColourId)); bounds.removeFromBottom(PADDING); juce::Path path; path.addRoundedRectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), OscirenderLookAndFeel::RECT_RADIUS, OscirenderLookAndFeel::RECT_RADIUS, true, false, true, false); g.fillPath(path); g.setColour(juce::Colours::white); // draw drag and drop handle using circles double size = 4; double leftPad = 4; double spacing = 7; double topPad = 6; double y = bounds.getHeight() / 2 - 15; g.fillEllipse(leftPad, y + topPad, size, size); g.fillEllipse(leftPad, y + topPad + spacing, size, size); g.fillEllipse(leftPad, y + topPad + 2 * spacing, size, size); g.fillEllipse(leftPad + spacing, y + topPad, size, size); g.fillEllipse(leftPad + spacing, y + topPad + spacing, size, size); g.fillEllipse(leftPad + spacing, y + topPad + 2 * spacing, size, size); DraggableListBoxItem::paint(g); } void EffectsListComponent::paintOverChildren(juce::Graphics& g) { g.setColour(juce::Colours::black.withAlpha(0.3f)); auto bounds = list.getBounds(); bounds.removeFromBottom(PADDING); juce::Path path; path.addRoundedRectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), OscirenderLookAndFeel::RECT_RADIUS, OscirenderLookAndFeel::RECT_RADIUS, false, true, false, true); if (!selected.getToggleState()) { g.fillPath(path); } } void EffectsListComponent::resized() { auto area = getLocalBounds(); auto leftBar = area.removeFromLeft(LEFT_BAR_WIDTH); leftBar.removeFromLeft(20); area.removeFromRight(PADDING); selected.setBounds(leftBar.withSizeKeepingCentre(30, 20)); list.setBounds(area); } std::shared_ptr EffectsListComponent::createComponent(EffectParameter* parameter) { if (parameter->paramID == "customEffectStrength") { std::shared_ptr button = std::make_shared(parameter->name, BinaryData::pencil_svg, juce::Colours::white, juce::Colours::red); std::weak_ptr weakButton = button; button->setEdgeIndent(5); button->setToggleState(editor.editingCustomFunction, juce::dontSendNotification); button->setTooltip("Toggles whether the text editor is editing the currently open file, or the custom Lua effect."); button->onClick = [this, weakButton] { if (auto button = weakButton.lock()) { editor.editCustomFunction(button->getToggleState()); } }; return button; } return nullptr; } int EffectsListBoxModel::getRowHeight(int row) { auto data = (AudioEffectListBoxItemData&)modelData; return data.getEffect(row)->parameters.size() * EffectsListComponent::ROW_HEIGHT + EffectsListComponent::PADDING; } bool EffectsListBoxModel::hasVariableHeightRows() const { return true; } juce::Component* EffectsListBoxModel::refreshComponentForRow(int rowNumber, bool isRowSelected, juce::Component *existingComponentToUpdate) { std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) { auto data = (AudioEffectListBoxItemData&)modelData; item = std::make_unique(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, *data.getEffect(rowNumber)); } return item.release(); }