Files
InstaDrums/Source/PluginEditor.cpp
hariel1985 20b9fe2674 GUI polish: 3D metal knobs, Rajdhani font, background texture, scaling UI
- Custom Rajdhani font (Regular/Medium/Bold) embedded via BinaryData
- Background carbon fiber noise texture overlay
- 3D metal knobs with radial gradient, rim, highlight, center cap
  - Orange type (ADSR/Master/Pitch/Pan) + Dark/blue type (FX/Filter)
  - Intense glow on value arc (5 layers: outer -> hot center)
  - Intense glow on pointer (4 layers)
  - All thicknesses scale proportionally with knob pixel size
- FX panel: bordered boxes for each section with gradient background
- Pad glow: cyan multi-pass glow on selected pad
- Pad numbers: dark background badge for contrast
- Waveform display: grid lines + center reference line
- VU meter: peak hold indicator + dB scale markers
- Buttons: gradient fill + hover highlight + rounded border
- All fonts and spacing scale proportionally with window size
- Top bar: darker header with gradient
- Double-click resets knobs to default values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 17:09:04 +01:00

264 sor
9.5 KiB
C++

#include "PluginEditor.h"
InstaDrumsEditor::InstaDrumsEditor (InstaDrumsProcessor& p)
: AudioProcessorEditor (&p), processor (p)
{
setLookAndFeel (&customLookAndFeel);
juce::LookAndFeel::setDefaultLookAndFeel (&customLookAndFeel);
// Title
titleLabel.setFont (customLookAndFeel.getBoldFont (25.0f));
titleLabel.setColour (juce::Label::textColourId, InstaDrumsLookAndFeel::accent);
titleLabel.setJustificationType (juce::Justification::centredLeft);
addAndMakeVisible (titleLabel);
versionLabel.setFont (juce::FontOptions (12.5f));
versionLabel.setColour (juce::Label::textColourId, InstaDrumsLookAndFeel::textSecondary);
versionLabel.setJustificationType (juce::Justification::centredRight);
addAndMakeVisible (versionLabel);
padsLabel.setFont (juce::FontOptions (12.5f, juce::Font::bold));
padsLabel.setColour (juce::Label::textColourId, InstaDrumsLookAndFeel::textSecondary);
addAndMakeVisible (padsLabel);
// Buttons
auto styleBtn = [this] (juce::TextButton& btn) {
btn.setColour (juce::TextButton::buttonColourId, InstaDrumsLookAndFeel::bgLight);
btn.setColour (juce::TextButton::textColourOffId, InstaDrumsLookAndFeel::textPrimary);
addAndMakeVisible (btn);
};
styleBtn (loadSampleButton);
styleBtn (saveKitButton);
styleBtn (loadKitButton);
styleBtn (loadFolderButton);
loadSampleButton.onClick = [this]
{
fileChooser = std::make_unique<juce::FileChooser> ("Load Sample", juce::File{},
"*.wav;*.aiff;*.aif;*.flac;*.ogg;*.mp3");
fileChooser->launchAsync (juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles,
[this] (const juce::FileChooser& fc) {
auto file = fc.getResult();
if (file.existsAsFile())
{
processor.loadSample (selectedPadIndex, file);
sampleEditor.updateFromPad();
}
});
};
saveKitButton.onClick = [this]
{
fileChooser = std::make_unique<juce::FileChooser> ("Save Kit", juce::File{}, "*.drumkit");
fileChooser->launchAsync (juce::FileBrowserComponent::saveMode,
[this] (const juce::FileChooser& fc) {
auto file = fc.getResult();
if (file != juce::File{})
processor.saveKitPreset (file.hasFileExtension (".drumkit") ? file : file.withFileExtension ("drumkit"));
});
};
loadKitButton.onClick = [this]
{
fileChooser = std::make_unique<juce::FileChooser> ("Load Kit", juce::File{}, "*.drumkit");
fileChooser->launchAsync (juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles,
[this] (const juce::FileChooser& fc) {
auto file = fc.getResult();
if (file.existsAsFile())
{
processor.loadKitPreset (file);
rebuildPadGrid();
selectPad (0);
}
});
};
loadFolderButton.onClick = [this]
{
fileChooser = std::make_unique<juce::FileChooser> ("Select Sample Folder", juce::File{});
fileChooser->launchAsync (juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectDirectories,
[this] (const juce::FileChooser& fc) {
auto folder = fc.getResult();
if (folder.isDirectory())
{
processor.loadKitFromFolder (folder);
rebuildPadGrid();
selectPad (0);
}
});
};
// Panels
addAndMakeVisible (sampleEditor);
addAndMakeVisible (fxPanel);
addAndMakeVisible (masterPanel);
rebuildPadGrid();
selectPad (0);
// Sizing
constrainer.setMinimumSize (800, 500);
constrainer.setMaximumSize (1920, 1080);
setConstrainer (&constrainer);
setSize (960, 600);
setResizable (true, true);
startTimerHz (30);
}
InstaDrumsEditor::~InstaDrumsEditor()
{
juce::LookAndFeel::setDefaultLookAndFeel (nullptr);
setLookAndFeel (nullptr);
}
void InstaDrumsEditor::rebuildPadGrid()
{
padComponents.clear();
auto loadCallback = [this] (int padIndex, const juce::File& file) {
processor.loadSample (padIndex, file);
if (padIndex == selectedPadIndex)
sampleEditor.updateFromPad();
};
for (int i = 0; i < processor.getNumPads(); ++i)
{
auto* pc = new PadComponent (processor.getPad (i), loadCallback, i);
pc->onSelected = [this] (int idx) { selectPad (idx); };
addAndMakeVisible (pc);
padComponents.add (pc);
}
resized();
}
void InstaDrumsEditor::selectPad (int index)
{
selectedPadIndex = index;
for (int i = 0; i < padComponents.size(); ++i)
padComponents[i]->setSelected (i == index);
if (index >= 0 && index < processor.getNumPads())
sampleEditor.setPad (&processor.getPad (index));
}
void InstaDrumsEditor::paint (juce::Graphics& g)
{
// Background gradient
juce::ColourGradient bgGrad (InstaDrumsLookAndFeel::bgDark, 0, 0,
InstaDrumsLookAndFeel::bgDark.darker (0.3f), 0, (float) getHeight(), false);
g.setGradientFill (bgGrad);
g.fillAll();
// Noise texture overlay
customLookAndFeel.drawBackgroundTexture (g, getLocalBounds());
// Top header bar
float sc = (float) getHeight() / 600.0f;
int topH = std::max (28, (int) (36 * sc));
g.setColour (InstaDrumsLookAndFeel::bgDark.darker (0.4f));
g.fillRect (0, 0, getWidth(), topH);
g.setColour (InstaDrumsLookAndFeel::bgLight.withAlpha (0.3f));
g.drawHorizontalLine (topH, 0, (float) getWidth());
// Divider lines
auto bounds = getLocalBounds();
int rightPanelX = (int) (bounds.getWidth() * 0.52f);
int masterH = std::max (44, (int) (60 * sc));
int bottomPanelY = bounds.getHeight() - masterH - 4;
g.setColour (InstaDrumsLookAndFeel::bgLight.withAlpha (0.4f));
g.drawVerticalLine (rightPanelX - 2, 30, (float) bottomPanelY);
g.drawHorizontalLine (bottomPanelY - 1, 0, (float) bounds.getWidth());
}
void InstaDrumsEditor::resized()
{
auto area = getLocalBounds();
float scale = (float) getHeight() / 600.0f;
// Top bar
int topBarH = std::max (28, (int) (36 * scale));
auto topBar = area.removeFromTop (topBarH).reduced (8, 4);
float titleSize = std::max (18.0f, 26.0f * scale);
titleLabel.setFont (customLookAndFeel.getBoldFont (titleSize));
titleLabel.setBounds (topBar.removeFromLeft ((int) (180 * scale)));
float smallSize = std::max (10.0f, 13.0f * scale);
versionLabel.setFont (juce::FontOptions (smallSize));
versionLabel.setBounds (topBar.removeFromRight ((int) (50 * scale)));
int btnW = std::max (60, (int) (90 * scale));
loadFolderButton.setBounds (topBar.removeFromRight (btnW).reduced (2));
loadKitButton.setBounds (topBar.removeFromRight ((int) (btnW * 0.8f)).reduced (2));
saveKitButton.setBounds (topBar.removeFromRight ((int) (btnW * 0.8f)).reduced (2));
loadSampleButton.setBounds (topBar.removeFromRight (btnW).reduced (2));
// Bottom master bar
int masterH = std::max (44, (int) (60 * scale));
masterPanel.setBounds (area.removeFromBottom (masterH).reduced (4, 2));
// Left panel: pad grid (~52% width)
int rightPanelX = (int) (area.getWidth() * 0.52f);
auto leftArea = area.removeFromLeft (rightPanelX).reduced (6);
// Pads label
int padsLabelH = (int) (20 * scale);
padsLabel.setFont (juce::FontOptions (std::max (11.0f, 14.0f * scale), juce::Font::bold));
auto padsHeader = leftArea.removeFromTop (padsLabelH);
padsLabel.setBounds (padsHeader);
// Pad grid
int numPads = padComponents.size();
if (numPads > 0)
{
int rows = (numPads + padColumns - 1) / padColumns;
int padW = leftArea.getWidth() / padColumns;
int padH = leftArea.getHeight() / rows;
for (int i = 0; i < numPads; ++i)
{
int row = i / padColumns;
int col = i % padColumns;
padComponents[i]->setBounds (leftArea.getX() + col * padW,
leftArea.getY() + row * padH,
padW, padH);
}
}
// Right panel: sample editor (top ~55%) + FX (bottom ~45%)
auto rightArea = area.reduced (4);
int editorHeight = (int) (rightArea.getHeight() * 0.55f);
sampleEditor.setBounds (rightArea.removeFromTop (editorHeight).reduced (0, 2));
fxPanel.setBounds (rightArea.reduced (0, 2));
}
void InstaDrumsEditor::timerCallback()
{
for (auto* pc : padComponents)
pc->repaint();
// Sync FX panel knobs -> processor atomic params
processor.compThreshold.store (fxPanel.getCompThreshold());
processor.compRatio.store (fxPanel.getCompRatio());
processor.eqLo.store (fxPanel.getEqLo());
processor.eqMid.store (fxPanel.getEqMid());
processor.eqHi.store (fxPanel.getEqHi());
processor.distDrive.store (fxPanel.getDistDrive());
processor.distMix.store (fxPanel.getDistMix());
processor.reverbSize.store (fxPanel.getReverbSize());
processor.reverbDecay.store (fxPanel.getReverbDecay());
// Sync master panel -> processor
processor.masterVolume.store (masterPanel.getMasterVolume());
processor.masterPan.store (masterPanel.getMasterPan());
processor.masterTune.store (masterPanel.getMasterTune());
// Update VU meter from processor
masterPanel.getVuMeter().setLevel (processor.vuLevelL.load(), processor.vuLevelR.load());
}