#pragma once #include #include "ui/SequencerMatrix.h" class MonoStepAudioProcessor; class EditorLookAndFeel final : public juce::LookAndFeel_V4 { public: EditorLookAndFeel(); void drawRotarySlider (juce::Graphics&, int x, int y, int w, int h, float sliderPos, float rotaryStartAngle, float rotaryEndAngle, juce::Slider&) override; void drawToggleButton (juce::Graphics&, juce::ToggleButton&, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override; void drawButtonBackground (juce::Graphics&, juce::Button&, const juce::Colour& backgroundColour, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override; void drawButtonText (juce::Graphics&, juce::TextButton&, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override; }; class Knob final : public juce::Component { public: explicit Knob (const juce::String& title); juce::Slider& getSlider() { return slider; } void attach (juce::AudioProcessorValueTreeState& apvts, const juce::String& paramId) { attachment = std::make_unique (apvts, paramId, slider); } void setFormat (std::function formatFn) { format = std::move (formatFn); updateValueLabel(); } void setOnValueChanged (std::function valueChangedFn) { customOnValueChange = std::move (valueChangedFn); } void resized() override; private: void updateValueLabel(); juce::Slider slider; juce::Label titleLabel; juce::Label valueLabel; std::unique_ptr attachment; std::function format; std::function customOnValueChange; }; class ChoiceBar final : public juce::Component, public juce::AudioProcessorValueTreeState::Listener { public: ChoiceBar (juce::AudioProcessorValueTreeState& apvts, const juce::String& paramId, const juce::StringArray& choices); ChoiceBar (const juce::StringArray& choices, std::function onSelected); ~ChoiceBar() override; void parameterChanged (const juce::String&, float) override; void setManualHandler (std::function handler) { manualHandler = std::move (handler); } void resized() override; private: void refreshFromParam(); void setIndex (int index); juce::AudioProcessorValueTreeState* apvts = nullptr; juce::String paramId; std::function manualHandler; juce::OwnedArray buttons; }; class StepPanel final : public juce::Component { public: explicit StepPanel (MonoStepAudioProcessor& processor); void setSelectedStep (int stepIdx); void refresh(); std::function onStepSelected; void resized() override; private: void clearPattern(); MonoStepAudioProcessor& processor; int selectedStep = 0; bool updating = false; juce::Label titleLabel; juce::Label noteLabel; juce::Label gateLabel; Knob fineKnob; juce::TextButton prevButton; juce::TextButton nextButton; juce::Label hintLabel; juce::TextButton clearButton; }; class MonoStepAudioProcessorEditor final : public juce::AudioProcessorEditor, public juce::Timer { public: explicit MonoStepAudioProcessorEditor (MonoStepAudioProcessor& processor); ~MonoStepAudioProcessorEditor() override; void paint (juce::Graphics&) override; void resized() override; void timerCallback() override; private: void paintContent (juce::Graphics&); class ContentComponent final : public juce::Component { public: explicit ContentComponent (MonoStepAudioProcessorEditor& owner) : ownerRef (owner) {} void paint (juce::Graphics& g) override { ownerRef.paintContent (g); } private: MonoStepAudioProcessorEditor& ownerRef; }; MonoStepAudioProcessor& processorRef; ContentComponent content; static constexpr int baseWidth = 1060; static constexpr int baseHeight = 640; float zoomFactor = 1.0f; void setZoomFactor (float newZoom); std::unique_ptr lnf; std::vector> attachments; Knob lengthKnob; Knob swingKnob; Knob rootKnob; Knob gateKnob; Knob masterKnob; Knob detuneKnob; Knob mixKnob; Knob cutoffKnob; Knob resKnob; Knob glideKnob; Knob attackKnob; Knob decayKnob; Knob sustainKnob; Knob releaseKnob; ChoiceBar rateBar; ChoiceBar octaveBar; ChoiceBar zoomBar; ChoiceBar wave1Bar; ChoiceBar wave2Bar; SequencerMatrix matrix; StepPanel stepPanel; int lastPlayStep = -1; void refreshEditor(); };