#pragma once #include "PluginProcessor.h" #include class SaftpumpeEditor : public juce::AudioProcessorEditor, public juce::Timer, public juce::ComboBox::Listener { public: SaftpumpeEditor(SaftpumpeProcessor&); ~SaftpumpeEditor() override; void paint(juce::Graphics&) override; void resized() override; void timerCallback() override; void comboBoxChanged(juce::ComboBox* box) override; private: SaftpumpeProcessor& processorRef; float uiScale = 1.5f; // Dark industrial palette juce::Colour bezelColour{0xff0a0a0c}; juce::Colour faceplateTop{0xff222226}; juce::Colour faceplateBot{0xff161618}; juce::Colour knobLight{0xff72727a}; juce::Colour knobDark{0xff3a3a40}; juce::Colour grooveDark{0xff111114}; juce::Colour greenLed{0xff00e676}; juce::Colour redLed{0xffe63333}; juce::Colour amberLed{0xffe6a600}; juce::Colour textColour{0xffb0b0b6}; juce::Colour textDim{0xff6a6a70}; juce::Colour textBright{0xffe0e0e4}; juce::Colour accentLine{0xff00e676}; juce::Colour lcdBg{0xff080c08}; juce::Colour lcdText{0xff00cc66}; juce::Slider thresholdSlider, ratioSlider, attackSlider, releaseSlider; juce::Slider kneeSlider, makeupSlider, mixSlider, autoReleaseSlider; juce::Label thresholdLabel, ratioLabel, attackLabel, releaseLabel; juce::Label kneeLabel, makeupLabel, mixLabel, autoReleaseLabel; // LCD value displays juce::Label thresholdLCD, ratioLCD, attackLCD, releaseLCD; juce::Label kneeLCD, makeupLCD, mixLCD, autoReleaseLCD; juce::ComboBox zoomBox; juce::Label zoomLabel; juce::ComboBox presetBox; juce::Label presetLabel; using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; std::unique_ptr thresholdAttachment, ratioAttachment, attackAttachment; std::unique_ptr releaseAttachment, kneeAttachment, makeupAttachment; std::unique_ptr mixAttachment, autoReleaseAttachment; juce::Rectangle meterArea; juce::Rectangle scopeArea; juce::Rectangle zoomArea; juce::Rectangle rowBounds[2]; juce::Rectangle knobBounds[8]; juce::Point knobCentre[8]; float knobRadius = 0.0f; struct PeakHold { float peak = 0.0f; double peakTime = 0.0; static constexpr double holdMs = 1000.0; } grPeak, inPeak, outPeak; void computeLayout(); void drawKnob(juce::Graphics& g, juce::Rectangle bounds, float normalizedValue, float arcFillFrac); void drawLCD(juce::Graphics& g, juce::Rectangle bounds, const juce::String& text); void drawMeter(juce::Graphics& g, juce::Rectangle bounds, float level, float minDb, float maxDb, bool isGainReduction); void drawPeakHold(juce::Graphics& g, juce::Rectangle bounds, float peakNorm, bool fromTop = false); void drawScope(juce::Graphics& g, juce::Rectangle bounds); void drawScrew(juce::Graphics& g, float cx, float cy, float radius); static constexpr float startAngle = juce::MathConstants::pi * 0.75f; static constexpr float endAngle = juce::MathConstants::pi * 2.25f; juce::String formatValue(int idx, float val); struct Preset { const char* name; float threshold, ratio, attack, release, knee, makeup, mix, autoRelease; }; static const std::vector& getPresets(); void applyPreset(int index); juce::Rectangle presetArea; struct NoDotLookAndFeel : juce::LookAndFeel_V4 { void drawRotarySlider(juce::Graphics&, int, int, int, int, float, float, float, juce::Slider&) override {} } lnf; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SaftpumpeEditor) };