#include "PluginEditor.h" TerionProcessorEditor::TerionProcessorEditor(TerionProcessor& p) : AudioProcessorEditor(p), processorRef(p), scope(p), fadeKnob(*p.apvts.getParameter("fadeTime")), sensitivityKnob(*p.apvts.getParameter("sensitivity")), dotSizeKnob(*p.apvts.getParameter("dotSize")), smoothingKnob(*p.apvts.getParameter("smoothing")) { setSize(kBaseWidth, kBaseHeight); setResizable(true, true); setResizeLimits(kBaseWidth, kBaseHeight, 1200, 1200); addAndMakeVisible(scope); // Fade knob addAndMakeVisible(fadeKnob); fadeKnob.setRange(0.1, 10.0); fadeAttachment = std::make_unique( processorRef.apvts, "fadeTime", fadeKnob); // Sensitivity knob addAndMakeVisible(sensitivityKnob); sensitivityKnob.setRange(-12.0, 24.0); sensitivityAttachment = std::make_unique( processorRef.apvts, "sensitivity", sensitivityKnob); // Dot size knob addAndMakeVisible(dotSizeKnob); dotSizeKnob.setRange(0.5, 3.0); dotSizeAttachment = std::make_unique( processorRef.apvts, "dotSize", dotSizeKnob); // Smoothing knob addAndMakeVisible(smoothingKnob); smoothingKnob.setRange(0.0, 0.99); smoothingAttachment = std::make_unique( processorRef.apvts, "smoothing", smoothingKnob); // Freeze button addAndMakeVisible(freezeButton); freezeButton.setButtonText("FRZ"); freezeButton.setColour(juce::ToggleButton::textColourId, juce::Colour(0xff888899)); freezeButton.setColour(juce::ToggleButton::tickColourId, juce::Colour(0xff7ecfcf)); freezeAttachment = std::make_unique( processorRef.apvts, "freeze", freezeButton); // Scale dropdown addAndMakeVisible(scaleBox); scaleBox.addItem("100%", 1); scaleBox.addItem("125%", 2); scaleBox.addItem("150%", 3); scaleBox.addItem("200%", 4); scaleBox.addItem("300%", 5); scaleBox.setSelectedId(2, juce::dontSendNotification); scaleBox.onChange = [this] { scaleChanged(); }; scaleChanged(); // Knob labels (separate from knobs to ensure correct z-order) auto setupLabel = [this](juce::Label& label, const juce::String& text) { addAndMakeVisible(label); label.setText(text, juce::dontSendNotification); label.setJustificationType(juce::Justification::centredTop); label.setFont(juce::Font(juce::FontOptions(9.0f))); label.setColour(juce::Label::textColourId, juce::Colour(0xff888899)); label.setInterceptsMouseClicks(false, false); }; setupLabel(fadeLabel, fadeKnob.getParameterName()); setupLabel(sensitivityLabel, sensitivityKnob.getParameterName()); setupLabel(dotSizeLabel, dotSizeKnob.getParameterName()); setupLabel(smoothingLabel, smoothingKnob.getParameterName()); } TerionProcessorEditor::~TerionProcessorEditor() {} void TerionProcessorEditor::scaleChanged() { int id = scaleBox.getSelectedId(); switch (id) { case 1: currentScale = 1.0f; break; case 2: currentScale = 1.25f; break; case 3: currentScale = 1.5f; break; case 4: currentScale = 2.0f; break; case 5: currentScale = 3.0f; break; default: currentScale = 1.25f; break; } setTransform(juce::AffineTransform::scale(currentScale)); setSize(static_cast(kBaseWidth * currentScale), static_cast(kBaseHeight * currentScale)); } void TerionProcessorEditor::paint(juce::Graphics& g) { g.fillAll(juce::Colour(0xff0a0a0f)); // Footer auto bounds = getLocalBounds(); auto footer = bounds.removeFromBottom(kFooterHeight); g.setColour(juce::Colour(0xff15151e)); g.fillRect(footer); g.setColour(juce::Colour(0xff555566)); g.setFont(9.0f); juce::String footerText = "Terion " + juce::String(TERION_GIT_HASH) + " | " + juce::String(TERION_BUILD_TIME) + " | " + juce::String(TERION_GIT_LOCAL_COMMITS) + " local"; g.drawFittedText(footerText, footer.reduced(10, 0), juce::Justification::centredLeft, 1); } void TerionProcessorEditor::resized() { auto bounds = getLocalBounds(); auto footer = bounds.removeFromBottom(kFooterHeight); // Footer: scale dropdown on the right scaleBox.setBounds(footer.getWidth() - 60, 1, 56, kFooterHeight - 2); // Scope fills the remaining area scope.setBounds(bounds); // Controls layout: top-right corner, horizontal row const int startX = bounds.getWidth() - (4 * (kKnobSize + kKnobMargin) + kKnobSize + kKnobMargin); const int startY = 10; fadeKnob.setBounds(startX, startY, kKnobSize, kKnobSize); sensitivityKnob.setBounds(startX + (kKnobSize + kKnobMargin), startY, kKnobSize, kKnobSize); dotSizeKnob.setBounds(startX + 2 * (kKnobSize + kKnobMargin), startY, kKnobSize, kKnobSize); smoothingKnob.setBounds(startX + 3 * (kKnobSize + kKnobMargin), startY, kKnobSize, kKnobSize); freezeButton.setBounds(startX + 4 * (kKnobSize + kKnobMargin), startY + 10, kKnobSize, kKnobSize - 20); const int labelY = startY + kKnobSize - 6; fadeLabel.setBounds(fadeKnob.getX(), labelY, kKnobSize, 12); sensitivityLabel.setBounds(sensitivityKnob.getX(), labelY, kKnobSize, 12); dotSizeLabel.setBounds(dotSizeKnob.getX(), labelY, kKnobSize, 12); smoothingLabel.setBounds(smoothingKnob.getX(), labelY, kKnobSize, 12); }