Add initial dummy effect grid

pull/307/head
James H Ball 2025-08-01 22:48:42 +01:00
rodzic 47ed50a96c
commit 83927204b8
10 zmienionych plików z 408 dodań i 88 usunięć

Wyświetl plik

@ -42,6 +42,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Multiplex Phase", "Controls the current phase of the multiplex grid animation.", "gridPhase", VERSION_HINT, 0.0, 0.0, 1.0),
new osci::EffectParameter("Multiplex Delay", "Controls the delay of the audio samples used in the multiplex effect.", "gridDelay", VERSION_HINT, 0.0, 0.0, 1.0),
});
multiplexEffect->setName("Multiplex");
// Set up the Grid Phase parameter with sawtooth LFO at 100Hz
multiplexEffect->getParameter("gridPhase")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
multiplexEffect->getParameter("gridPhase")->lfoRate->setUnnormalisedValueNotifyingHost(100.0);
@ -58,6 +59,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Scale Y", "Scales the object in the vertical direction.", "scaleY", VERSION_HINT, 1.0, -3.0, 3.0),
new osci::EffectParameter("Scale Z", "Scales the depth of the object.", "scaleZ", VERSION_HINT, 1.0, -3.0, 3.0),
});
scaleEffect->setName("Scale");
scaleEffect->markLockable(true);
booleanParameters.push_back(scaleEffect->linked);
toggleableEffects.push_back(scaleEffect);
@ -72,6 +74,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Distort Y", "Distorts the image in the vertical direction by jittering the audio sample being drawn.", "distortY", VERSION_HINT, 0.0, 0.0, 1.0),
new osci::EffectParameter("Distort Z", "Distorts the depth of the image by jittering the audio sample being drawn.", "distortZ", VERSION_HINT, 0.1, 0.0, 1.0),
});
distortEffect->setName("Distort");
distortEffect->markLockable(false);
booleanParameters.push_back(distortEffect->linked);
toggleableEffects.push_back(distortEffect);
@ -87,6 +90,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Ripple Phase", "Controls the position of the ripple. Animate this to see a moving ripple effect.", "ripplePhase", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Ripple Amount", "Controls how many ripples are applied to the image.", "rippleAmount", VERSION_HINT, 0.1, 0.0, 1.0),
});
rippleEffect->setName("Ripple");
rippleEffect->getParameter("ripplePhase")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
toggleableEffects.push_back(rippleEffect);
auto rotateEffect = std::make_shared<osci::Effect>(
@ -99,10 +103,11 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Rotate Y", "Controls the rotation of the object in the Y axis.", "rotateY", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Rotate Z", "Controls the rotation of the object in the Z axis.", "rotateZ", VERSION_HINT, 0.0, -1.0, 1.0),
});
rotateEffect->setName("Rotate");
rotateEffect->getParameter("rotateY")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
rotateEffect->getParameter("rotateY")->lfoRate->setUnnormalisedValueNotifyingHost(0.2);
toggleableEffects.push_back(rotateEffect);
toggleableEffects.push_back(std::make_shared<osci::Effect>(
auto translateEffect = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
return input + osci::Point(values[0], values[1], values[2]);
},
@ -110,7 +115,9 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Translate X", "Moves the object horizontally.", "translateX", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Translate Y", "Moves the object vertically.", "translateY", VERSION_HINT, 0.0, -1.0, 1.0),
new osci::EffectParameter("Translate Z", "Moves the object away from the camera.", "translateZ", VERSION_HINT, 0.0, -1.0, 1.0),
}));
});
translateEffect->setName("Translate");
toggleableEffects.push_back(translateEffect);
toggleableEffects.push_back(std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
double length = 10 * values[0] * input.magnitude();
@ -130,18 +137,24 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Wobble Amount", "Adds a sine wave of the prominent frequency in the audio currently playing. The sine wave's frequency is slightly offset to create a subtle 'wobble' in the image. Increasing the slider increases the strength of the wobble.", "wobble", VERSION_HINT, 0.3, 0.0, 1.0),
new osci::EffectParameter("Wobble Phase", "Controls the phase of the wobble.", "wobblePhase", VERSION_HINT, 0.0, -1.0, 1.0),
});
wobble->setName("Wobble");
wobble->getParameter("wobblePhase")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);
toggleableEffects.push_back(wobble);
toggleableEffects.push_back(std::make_shared<osci::Effect>(
auto delay = std::make_shared<osci::Effect>(
delayEffect,
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Delay Decay", "Adds repetitions, delays, or echos to the audio. This slider controls the volume of the echo.", "delayDecay", VERSION_HINT, 0.4, 0.0, 1.0),
new osci::EffectParameter("Delay Length", "Controls the time in seconds between echos.", "delayLength", VERSION_HINT, 0.5, 0.0, 1.0)}));
toggleableEffects.push_back(std::make_shared<osci::Effect>(
new osci::EffectParameter("Delay Length", "Controls the time in seconds between echos.", "delayLength", VERSION_HINT, 0.5, 0.0, 1.0)
});
delay->setName("Delay");
toggleableEffects.push_back(delay);
auto dashEffect = std::make_shared<osci::Effect>(
dashedLineEffect,
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Dash Length", "Controls the length of the dashed line.", "dashLength", VERSION_HINT, 0.2, 0.0, 1.0),
}));
});
dashEffect->setName("Dash");
toggleableEffects.push_back(dashEffect);
toggleableEffects.push_back(custom);
toggleableEffects.push_back(trace);
trace->getParameter("traceLength")->lfo->setUnnormalisedValueNotifyingHost((int)osci::LfoType::Sawtooth);

Wyświetl plik

@ -28,6 +28,7 @@ EffectComponent::EffectComponent(osci::Effect& effect, int index) : effect(effec
slider.setSliderStyle(juce::Slider::LinearHorizontal);
slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, TEXT_BOX_WIDTH, slider.getTextBoxHeight());
slider.setScrollWheelEnabled(false);
if (effect.parameters[index]->step == 1.0) {
slider.setNumDecimalPlacesToDisplay(0);
} else {
@ -39,6 +40,7 @@ EffectComponent::EffectComponent(osci::Effect& effect, int index) : effect(effec
lfoSlider.setTextValueSuffix("Hz");
lfoSlider.setColour(sliderThumbOutlineColourId, juce::Colour(0xff00ff00));
lfoSlider.setNumDecimalPlacesToDisplay(3);
lfoSlider.setScrollWheelEnabled(false);
label.setFont(juce::Font(14.0f));

Wyświetl plik

@ -0,0 +1,81 @@
#include "EffectTypeGridComponent.h"
#include "../LookAndFeel.h"
EffectTypeGridComponent::EffectTypeGridComponent(OscirenderAudioProcessor& processor)
: audioProcessor(processor)
{
setupEffectItems();
setSize(400, 200); // Default size, will be resized by parent
setMouseCursor(juce::MouseCursor::PointingHandCursor);
}
EffectTypeGridComponent::~EffectTypeGridComponent() = default;
void EffectTypeGridComponent::setupEffectItems()
{
// Clear existing items
effectItems.clear();
// Get effect types directly from the audio processor's toggleableEffects
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);
for (const auto& effect : audioProcessor.toggleableEffects)
{
// Extract effect name from the effect ID or first parameter name
juce::String effectName = effect->getName();
// Create new item component
auto* item = new EffectTypeItemComponent(effectName, effect->getId());
// Set up callback to forward effect selection
item->onEffectSelected = [this](const juce::String& effectId) {
if (onEffectSelected)
onEffectSelected(effectId);
};
effectItems.add(item);
addAndMakeVisible(item);
}
}
void EffectTypeGridComponent::paint(juce::Graphics& g)
{
// No background - make component transparent
}
void EffectTypeGridComponent::resized()
{
auto bounds = getLocalBounds();
// Create FlexBox for responsive grid layout
flexBox = juce::FlexBox();
flexBox.flexWrap = juce::FlexBox::Wrap::wrap;
flexBox.justifyContent = juce::FlexBox::JustifyContent::spaceBetween;
flexBox.alignContent = juce::FlexBox::AlignContent::flexStart;
flexBox.flexDirection = juce::FlexBox::Direction::row;
// Add each effect item as a FlexItem with flex-grow to fill available space
for (auto* item : effectItems)
{
flexBox.items.add(juce::FlexItem(*item)
.withMinWidth(MIN_ITEM_WIDTH)
.withHeight(ITEM_HEIGHT)
.withFlex(1.0f) // Allow items to grow to fill available space
.withMargin(juce::FlexItem::Margin(0)));
}
flexBox.performLayout(bounds.toFloat());
}
int EffectTypeGridComponent::calculateRequiredHeight(int availableWidth) const
{
if (effectItems.isEmpty())
return ITEM_HEIGHT;
// Calculate how many items can fit per row
int itemsPerRow = juce::jmax(1, availableWidth / MIN_ITEM_WIDTH);
// Calculate number of rows needed
int numRows = (effectItems.size() + itemsPerRow - 1) / itemsPerRow; // Ceiling division
return numRows * ITEM_HEIGHT;
}

Wyświetl plik

@ -0,0 +1,29 @@
#pragma once
#include <JuceHeader.h>
#include "../PluginProcessor.h"
#include "EffectTypeItemComponent.h"
class EffectTypeGridComponent : public juce::Component
{
public:
EffectTypeGridComponent(OscirenderAudioProcessor& processor);
~EffectTypeGridComponent() override;
void paint(juce::Graphics& g) override;
void resized() override;
int calculateRequiredHeight(int availableWidth) const;
std::function<void(const juce::String& effectId)> onEffectSelected;
private:
OscirenderAudioProcessor& audioProcessor;
juce::OwnedArray<EffectTypeItemComponent> effectItems;
juce::FlexBox flexBox;
static constexpr int ITEM_HEIGHT = 80;
static constexpr int MIN_ITEM_WIDTH = 180;
void setupEffectItems();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectTypeGridComponent)
};

Wyświetl plik

@ -0,0 +1,120 @@
#include "EffectTypeItemComponent.h"
EffectTypeItemComponent::EffectTypeItemComponent(const juce::String& name, const juce::String& id)
: effectName(name), effectId(id),
hoverAnimator(juce::ValueAnimatorBuilder{}
.withEasing(juce::Easings::createEaseOut())
.withDurationMs(200)
.withValueChangedCallback([this](auto value) {
animationProgress = static_cast<float>(value);
repaint();
})
.build()),
unhoverAnimator(juce::ValueAnimatorBuilder{}
.withEasing(juce::Easings::createEaseOut())
.withDurationMs(200)
.withValueChangedCallback([this](auto value) {
animationProgress = 1.0f - static_cast<float>(value);
repaint();
})
.build())
{
setupAnimators();
}
EffectTypeItemComponent::~EffectTypeItemComponent() = default;
void EffectTypeItemComponent::setupAnimators()
{
animatorUpdater.addAnimator(hoverAnimator);
animatorUpdater.addAnimator(unhoverAnimator);
}
void EffectTypeItemComponent::animateHover(bool isHovering)
{
if (isHovering)
{
unhoverAnimator.complete();
hoverAnimator.start();
}
else
{
hoverAnimator.complete();
unhoverAnimator.start();
}
}
void EffectTypeItemComponent::paint(juce::Graphics& g)
{
auto bounds = getLocalBounds().toFloat().reduced(10);
// Apply upward shift based on animation progress
auto yOffset = -animationProgress * HOVER_LIFT_AMOUNT;
bounds = bounds.translated(0, yOffset);
// Draw drop shadow
if (animationProgress > 0.01f) {
juce::DropShadow shadow;
shadow.colour = juce::Colours::lime.withAlpha(animationProgress * 0.2f);
shadow.radius = 15 * animationProgress;
shadow.offset = juce::Point<int>(0, 4);
juce::Path shadowPath;
shadowPath.addRoundedRectangle(bounds.toFloat(), CORNER_RADIUS);
shadow.drawForPath(g, shadowPath);
}
// Draw background with rounded corners - interpolate between normal and hover colors
juce::Colour normalBgColour = juce::Colour::fromRGB(25, 25, 25);
juce::Colour hoverBgColour = juce::Colour::fromRGB(40, 40, 40);
juce::Colour bgColour = normalBgColour.interpolatedWith(hoverBgColour, animationProgress);
g.setColour(bgColour);
g.fillRoundedRectangle(bounds.toFloat(), CORNER_RADIUS);
// Draw colored outline
juce::Colour outlineColour = juce::Colour::fromRGB(160, 160, 160);
g.setColour(outlineColour.withAlpha(0.9f));
g.drawRoundedRectangle(bounds.toFloat(), CORNER_RADIUS, 1.0f);
// Create areas for text (left) and icon (right)
auto textArea = bounds.reduced(8, 4);
auto iconArea = textArea.removeFromRight(20); // Reserve 20px for icon on right
textArea = textArea.withTrimmedRight(4); // Add small gap between text and icon
g.setColour(juce::Colours::white);
g.setFont(juce::FontOptions(16.0f, juce::Font::plain));
g.drawText(effectName, textArea, juce::Justification::centred, true);
// Draw placeholder icon (simple circle with "+" symbol)
g.setColour(juce::Colours::white.withAlpha(0.7f));
auto iconSize = juce::jmin(iconArea.getWidth(), iconArea.getHeight()) - 4;
auto iconBounds = iconArea.withSizeKeepingCentre(iconSize, iconSize);
g.drawEllipse(iconBounds.toFloat(), 1.5f);
// Draw "+" symbol in the circle
auto centerX = iconBounds.getCentreX();
auto centerY = iconBounds.getCentreY();
auto halfSize = iconSize * 0.25f;
g.drawLine(centerX - halfSize, centerY, centerX + halfSize, centerY, 2.0f);
g.drawLine(centerX, centerY - halfSize, centerX, centerY + halfSize, 2.0f);
}
void EffectTypeItemComponent::mouseEnter(const juce::MouseEvent& event)
{
isHovered = true;
animateHover(true);
}
void EffectTypeItemComponent::mouseExit(const juce::MouseEvent& event)
{
isHovered = false;
animateHover(false);
}
void EffectTypeItemComponent::mouseDown(const juce::MouseEvent& event)
{
if (onEffectSelected)
onEffectSelected(effectId);
}

Wyświetl plik

@ -0,0 +1,38 @@
#pragma once
#include <JuceHeader.h>
class EffectTypeItemComponent : public juce::Component
{
public:
EffectTypeItemComponent(const juce::String& name, const juce::String& id);
~EffectTypeItemComponent() override;
void paint(juce::Graphics& g) override;
void mouseEnter(const juce::MouseEvent& event) override;
void mouseExit(const juce::MouseEvent& event) override;
void mouseDown(const juce::MouseEvent& event) override;
const juce::String& getEffectId() const { return effectId; }
const juce::String& getEffectName() const { return effectName; }
std::function<void(const juce::String& effectId)> onEffectSelected;
private:
juce::String effectName;
juce::String effectId;
float animationProgress = 0.0f;
bool isHovered = false;
// Animation components
juce::VBlankAnimatorUpdater animatorUpdater { this };
juce::Animator hoverAnimator;
juce::Animator unhoverAnimator;
static constexpr int CORNER_RADIUS = 8;
static constexpr float HOVER_LIFT_AMOUNT = 2.0f;
void setupAnimators();
void animateHover(bool isHovering);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectTypeItemComponent)
};

Wyświetl plik

@ -2,27 +2,28 @@
#include "SvgButton.h"
#include "../PluginEditor.h"
#include "../LookAndFeel.h"
#include "EffectTypeGridComponent.h"
EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, osci::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> effectComponent = std::make_shared<EffectComponent>(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<EffectComponent> weakEffectComponent = effectComponent;
effectComponent->slider.setValue(parameters[i]->getValueUnnormalised(), juce::dontSendNotification);
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());
for (int i = 0; i < parameters.size(); i++) {
std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(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<EffectComponent> weakEffectComponent = effectComponent;
effectComponent->slider.setValue(parameters[i]->getValueUnnormalised(), juce::dontSendNotification);
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();
};
};
effectComponent->updateToggleState = [this, i, weakEffectComponent] {
if (auto effectComponent = weakEffectComponent.lock()) {
selected.setToggleState(effectComponent->effect.enabled == nullptr || effectComponent->effect.enabled->getValue(), juce::dontSendNotification);
@ -31,20 +32,20 @@ effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) {
repaint();
};
auto component = createComponent(parameters[i]);
if (component != nullptr) {
auto component = createComponent(parameters[i]);
if (component != nullptr) {
effectComponent->setComponent(component);
}
listModel.addComponent(effectComponent);
}
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);
list.setModel(&listModel);
list.setRowHeight(ROW_HEIGHT);
list.updateContent();
addAndMakeVisible(list);
addAndMakeVisible(selected);
}
EffectsListComponent::~EffectsListComponent() {
@ -52,26 +53,26 @@ EffectsListComponent::~EffectsListComponent() {
}
void EffectsListComponent::paint(juce::Graphics& g) {
auto bounds = getLocalBounds().removeFromLeft(LEFT_BAR_WIDTH);
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);
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) {
@ -81,51 +82,78 @@ void EffectsListComponent::paintOverChildren(juce::Graphics& g) {
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()) {
if (!selected.getToggleState()) {
g.fillPath(path);
}
}
void EffectsListComponent::resized() {
auto area = getLocalBounds();
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);
selected.setBounds(leftBar.withSizeKeepingCentre(30, 20));
list.setBounds(area);
}
std::shared_ptr<juce::Component> EffectsListComponent::createComponent(osci::EffectParameter* parameter) {
if (parameter->paramID == "customEffectStrength") {
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, juce::Colours::white, juce::Colours::red);
std::weak_ptr<SvgButton> 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()) {
if (parameter->paramID == "customEffectStrength") {
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, juce::Colours::white, juce::Colours::red);
std::weak_ptr<SvgButton> 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;
};
return button;
}
return nullptr;
}
int EffectsListBoxModel::getRowHeight(int row) {
auto data = (AudioEffectListBoxItemData&)modelData;
return data.getEffect(row)->parameters.size() * EffectsListComponent::ROW_HEIGHT + EffectsListComponent::PADDING;
auto data = (AudioEffectListBoxItemData&)modelData;
if (row == data.getNumItems() - 1) {
// Effect type grid row - calculate dynamic height based on layout
// Get the available width from the listbox
int availableWidth = listBox.getWidth() - 20; // Account for scrollbar and margins
// Create a temporary grid component to calculate required height
EffectTypeGridComponent tempGrid(data.audioProcessor);
return tempGrid.calculateRequiredHeight(availableWidth);
}
return data.getEffect(row)->parameters.size() * EffectsListComponent::ROW_HEIGHT + EffectsListComponent::PADDING;
}
bool EffectsListBoxModel::hasVariableHeightRows() const {
return true;
return true;
}
juce::Component* EffectsListBoxModel::refreshComponentForRow(int rowNumber, bool isRowSelected, juce::Component *existingComponentToUpdate) {
std::unique_ptr<EffectsListComponent> item(dynamic_cast<EffectsListComponent*>(existingComponentToUpdate));
if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) {
auto data = (AudioEffectListBoxItemData&)modelData;
item = std::make_unique<EffectsListComponent>(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, *data.getEffect(rowNumber));
auto data = (AudioEffectListBoxItemData&)modelData;
if (juce::isPositiveAndBelow(rowNumber, data.getNumItems() - 1)) {
// Regular effect component
std::unique_ptr<EffectsListComponent> item(dynamic_cast<EffectsListComponent*>(existingComponentToUpdate));
item = std::make_unique<EffectsListComponent>(listBox, data, rowNumber, *data.getEffect(rowNumber));
return item.release();
} else if (rowNumber == data.getNumItems() - 1) {
// Create the effect type grid component
std::unique_ptr<EffectTypeGridComponent> gridComponent(dynamic_cast<EffectTypeGridComponent*>(existingComponentToUpdate));
if (gridComponent == nullptr) {
gridComponent = std::make_unique<EffectTypeGridComponent>(data.audioProcessor);
// Set up callback for when an effect is selected
gridComponent->onEffectSelected = [&data](const juce::String& effectId) {
// TODO: Implement adding new effect instance based on effectId
// This would need to be implemented based on how effects are created in the system
DBG("Effect selected: " + effectId);
};
}
return gridComponent.release();
}
return item.release();
return nullptr;
}

Wyświetl plik

@ -5,6 +5,7 @@
#include "EffectComponent.h"
#include "ComponentList.h"
#include "SwitchButton.h"
#include "EffectTypeGridComponent.h"
#include <random>
// Application-specific data container
@ -22,9 +23,9 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
void randomise() {
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);
for (int i = 0; i < data.size(); i++) {
auto effect = data[i];
auto id = effect->getId().toLowerCase();
for (int i = 0; i < data.size(); i++) {
auto effect = data[i];
auto id = effect->getId().toLowerCase();
if (id.contains("scale") || id.contains("translate") || id.contains("trace")) {
continue;
@ -42,19 +43,19 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
}
}
}
effect->enabled->setValueNotifyingHost(juce::Random::getSystemRandom().nextFloat() > 0.7);
}
effect->enabled->setValueNotifyingHost(juce::Random::getSystemRandom().nextFloat() > 0.7);
}
// shuffle precedence
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(data.begin(), data.end(), g);
std::shuffle(data.begin(), data.end(), g);
for (int i = 0; i < data.size(); i++) {
data[i]->setPrecedence(i);
}
for (int i = 0; i < data.size(); i++) {
data[i]->setPrecedence(i);
}
audioProcessor.updateEffectPrecedence();
audioProcessor.updateEffectPrecedence();
}
void resetData() {
@ -68,12 +69,12 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
}
int getNumItems() override {
return data.size();
return data.size() + 1;
}
// CURRENTLY NOT USED
void deleteItem(int indexOfItemToDelete) override {
// data.erase(data.begin() + indexOfItemToDelete);
// data.erase(data.begin() + indexOfItemToDelete);
}
// CURRENTLY NOT USED
@ -81,10 +82,10 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
// data.push_back(juce::String("Yahoo"));
}
void moveBefore(int indexOfItemToMove, int indexOfItemToPlaceBefore) override {
void moveBefore(int indexOfItemToMove, int indexOfItemToPlaceBefore) override {
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);
auto effect = data[indexOfItemToMove];
auto effect = data[indexOfItemToMove];
if (indexOfItemToMove < indexOfItemToPlaceBefore) {
move(data, indexOfItemToMove, indexOfItemToPlaceBefore - 1);
@ -92,12 +93,12 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
move(data, indexOfItemToMove, indexOfItemToPlaceBefore);
}
for (int i = 0; i < data.size(); i++) {
data[i]->setPrecedence(i);
}
for (int i = 0; i < data.size(); i++) {
data[i]->setPrecedence(i);
}
audioProcessor.updateEffectPrecedence();
}
}
void moveAfter(int indexOfItemToMove, int indexOfItemToPlaceAfter) override {
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);

@ -1 +1 @@
Subproject commit 913c3b052404818c45ad93c5e6022244d57df5e9
Subproject commit 56a625fcead4d7fd428a75afddf541363a0adf63

Wyświetl plik

@ -157,6 +157,14 @@
file="Source/components/EffectsListComponent.cpp"/>
<FILE id="dcLchL" name="EffectsListComponent.h" compile="0" resource="0"
file="Source/components/EffectsListComponent.h"/>
<FILE id="xQQo5D" name="EffectTypeGridComponent.cpp" compile="1" resource="0"
file="Source/components/EffectTypeGridComponent.cpp"/>
<FILE id="Fb2Qci" name="EffectTypeGridComponent.h" compile="0" resource="0"
file="Source/components/EffectTypeGridComponent.h"/>
<FILE id="PPK1iQ" name="EffectTypeItemComponent.cpp" compile="1" resource="0"
file="Source/components/EffectTypeItemComponent.cpp"/>
<FILE id="kGAfsx" name="EffectTypeItemComponent.h" compile="0" resource="0"
file="Source/components/EffectTypeItemComponent.h"/>
<FILE id="aEprcE" name="ErrorCodeEditorComponent.h" compile="0" resource="0"
file="Source/components/ErrorCodeEditorComponent.h"/>
<FILE id="L9DIT2" name="LabelledTextBox.h" compile="0" resource="0"