kopia lustrzana https://github.com/jameshball/osci-render
Add initial dummy effect grid
rodzic
47ed50a96c
commit
83927204b8
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
Ładowanie…
Reference in New Issue