2026-08-10 12:55:02 +02:00
|
|
|
#include "Knob.h"
|
|
|
|
|
|
|
|
|
|
Knob::Knob (const juce::String& title,
|
|
|
|
|
juce::AudioProcessorValueTreeState& apvts,
|
|
|
|
|
const juce::String& paramID,
|
|
|
|
|
float defaultValue,
|
|
|
|
|
std::function<juce::String (float)> formatFn,
|
|
|
|
|
const juce::String& tooltip)
|
|
|
|
|
: format (std::move (formatFn))
|
|
|
|
|
{
|
|
|
|
|
nameLabel.setText (title, juce::dontSendNotification);
|
|
|
|
|
nameLabel.setJustificationType (juce::Justification::centred);
|
|
|
|
|
nameLabel.setFont (juce::Font (juce::FontOptions (11.0f, juce::Font::bold)));
|
|
|
|
|
nameLabel.setColour (juce::Label::textColourId, juce::Colour (0xFFB8CCCC));
|
|
|
|
|
nameLabel.setInterceptsMouseClicks (false, false);
|
|
|
|
|
addAndMakeVisible (nameLabel);
|
|
|
|
|
|
|
|
|
|
valueLabel.setJustificationType (juce::Justification::centred);
|
|
|
|
|
valueLabel.setFont (juce::Font (juce::FontOptions (11.0f, juce::Font::bold)));
|
|
|
|
|
valueLabel.setColour (juce::Label::textColourId, juce::Colour (0xFF3CE0C8));
|
|
|
|
|
valueLabel.setInterceptsMouseClicks (false, false);
|
|
|
|
|
addAndMakeVisible (valueLabel);
|
|
|
|
|
|
|
|
|
|
slider.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
|
|
|
|
|
slider.setTextBoxStyle (juce::Slider::NoTextBox, true, 0, 0);
|
|
|
|
|
slider.setDoubleClickReturnValue (true, defaultValue);
|
|
|
|
|
slider.setWantsKeyboardFocus (true);
|
|
|
|
|
slider.setScrollWheelEnabled (true);
|
|
|
|
|
slider.setTooltip (tooltip);
|
|
|
|
|
slider.onValueChange = [this] { updateValueLabel(); };
|
|
|
|
|
addAndMakeVisible (slider);
|
|
|
|
|
|
|
|
|
|
attachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment> (apvts, paramID, slider);
|
|
|
|
|
|
|
|
|
|
updateValueLabel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Knob::setScaleFactor (float scale)
|
|
|
|
|
{
|
|
|
|
|
scaleFactor = scale;
|
|
|
|
|
resized();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Knob::updateValueLabel()
|
|
|
|
|
{
|
|
|
|
|
valueLabel.setText (format (slider.getValue()), juce::dontSendNotification);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Knob::paint (juce::Graphics& g)
|
|
|
|
|
{
|
|
|
|
|
auto b = slider.getBounds().toFloat();
|
|
|
|
|
auto shadow = b.translated (0.0f, b.getHeight() * 0.05f).expanded (b.getWidth() * 0.05f);
|
|
|
|
|
auto shadowCentre = shadow.getCentre().withY (shadow.getCentre().getY() + b.getHeight() * 0.4f);
|
|
|
|
|
auto grad = juce::ColourGradient (juce::Colour (0x60000000), shadowCentre,
|
|
|
|
|
juce::Colour (0x00000000), shadowCentre.translated (shadow.getWidth() * 0.7f, 0.0f),
|
|
|
|
|
true);
|
|
|
|
|
grad.addColour (0.55f, juce::Colour (0x20000000));
|
|
|
|
|
g.setGradientFill (grad);
|
|
|
|
|
g.fillEllipse (shadow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Knob::resized()
|
|
|
|
|
{
|
|
|
|
|
auto bounds = getLocalBounds();
|
|
|
|
|
const float s = scaleFactor;
|
|
|
|
|
|
|
|
|
|
const int nameH = juce::roundToInt (18.0f * s);
|
|
|
|
|
const int valueH = juce::roundToInt (16.0f * s);
|
|
|
|
|
const int maxKnobSize = juce::roundToInt (84.0f * s);
|
|
|
|
|
|
2026-08-10 15:36:11 +02:00
|
|
|
bounds.removeFromTop (juce::roundToInt (15.0f * s));
|
|
|
|
|
|
2026-08-10 12:55:02 +02:00
|
|
|
auto nameArea = bounds.removeFromTop (nameH);
|
2026-08-10 15:36:11 +02:00
|
|
|
bounds.removeFromBottom (juce::roundToInt (10.0f * s));
|
2026-08-10 12:55:02 +02:00
|
|
|
auto valueArea = bounds.removeFromBottom (valueH);
|
|
|
|
|
nameLabel.setBounds (nameArea);
|
|
|
|
|
valueLabel.setBounds (valueArea);
|
|
|
|
|
|
|
|
|
|
nameLabel.setFont (juce::Font (juce::FontOptions (juce::roundToInt (11.0f * s), juce::Font::bold)));
|
|
|
|
|
valueLabel.setFont (juce::Font (juce::FontOptions (juce::roundToInt (11.0f * s), juce::Font::bold)));
|
|
|
|
|
|
|
|
|
|
const int knobSize = jmin (bounds.getWidth(), bounds.getHeight(), maxKnobSize);
|
|
|
|
|
slider.setBounds (bounds.getCentreX() - knobSize / 2, bounds.getCentreY() - knobSize / 2, knobSize, knobSize);
|
|
|
|
|
}
|