#pragma once #include "PluginProcessor.h" #include class TrommelkisteLookAndFeel : public juce::LookAndFeel_V4 { public: juce::Colour bg{0xFF1A1A1A}; juce::Colour rowAlt{0xFF222222}; juce::Colour accent{0xFFE8600A}; float fontScale = 1.5f; void drawRotarySlider(juce::Graphics&, int x, int y, int w, int h, float pos, float start, float end, juce::Slider&) override; void drawToggleButton(juce::Graphics&, juce::ToggleButton&, bool, bool) override; void drawButtonBackground(juce::Graphics&, juce::Button&, const juce::Colour&, bool, bool) override; void drawComboBox(juce::Graphics&, int w, int h, bool down, int bx, int by, int bw, int bh, juce::ComboBox&) override; void positionComboBoxText(juce::ComboBox&, juce::Label&) override; }; struct KnobSlider : juce::Slider { std::function formatFn; juce::String getTextFromValue(double v) override { if (formatFn) return formatFn(v); return Slider::getTextFromValue(v); } }; class TrommelkisteEditor : public juce::AudioProcessorEditor, private juce::Timer, public juce::FileDragAndDropTarget { public: TrommelkisteEditor(TrommelkisteProcessor&); ~TrommelkisteEditor() override; void paint(juce::Graphics&) override; void resized() override; void mouseDown(const juce::MouseEvent&) override; void timerCallback() override; private: TrommelkisteProcessor& proc; TrommelkisteLookAndFeel lnf; juce::ComboBox scaleCombo; float uiScale = 1.5f; int scaled(int v) const { return (int)(v * uiScale + 0.5f); } void scaleChanged(); struct TrackUI { using SliderAtt = juce::AudioProcessorValueTreeState::SliderAttachment; using ButtonAtt = juce::AudioProcessorValueTreeState::ButtonAttachment; juce::TextButton prevBtn{"<"}; juce::TextButton nextBtn{">"}; KnobSlider level, length, velocity, pitch, tone, pan; KnobSlider delaySend, reverbSend, ringMod, destruction, shuffle; juce::ToggleButton decay; juce::Label fileLabel; std::unique_ptr levelAtt, lengthAtt, velAtt, pitchAtt, toneAtt, panAtt; std::unique_ptr delaySendAtt, reverbSendAtt; std::unique_ptr ringModAtt, destructionAtt, shuffleAtt; std::unique_ptr decayAtt; }; TrackUI ui[NUM_TRACKS]; juce::FileChooser chooser{"Load Sample", juce::File{}, "*.wav;*.aif;*.aiff;*.flac"}; double trackActivatedTime[NUM_TRACKS] = {}; // Preset selector juce::ComboBox presetCombo; juce::TextButton presetPrevBtn{"<"}; juce::TextButton presetNextBtn{">"}; // Global effect controls in header KnobSlider globalRingFreq, globalDestruction; std::unique_ptr globalRingFreqAtt, globalDestructionAtt; // Master controls in header KnobSlider masterVolume, masterPan, masterDelay, masterReverb; std::unique_ptr masterVolumeAtt, masterPanAtt, masterDelayAtt, masterReverbAtt; // Global shuffle controls in header KnobSlider globalShuffleDiv, globalShuffleAmt, globalGroove; std::unique_ptr globalShuffleDivAtt, globalShuffleAmtAtt, globalGrooveAtt; // Delay sync control in header KnobSlider delaySyncDiv; std::unique_ptr delaySyncDivAtt; void loadForTrack(int idx); void updateFileLabel(int idx); void refreshPresetCombo(); int getSlotIndexForPosition(int x, int y) const; int dropHoverIndex = -1; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TrommelkisteEditor) };