mirror of
https://codeberg.org/armin/ambivalence.git
synced 2026-09-01 12:20:48 +02:00
176 lines
No EOL
7.2 KiB
C++
176 lines
No EOL
7.2 KiB
C++
#include "AmbivalenceUI.h"
|
|
#include "../PluginProcessor.h"
|
|
|
|
// --- AmbivalenceLookAndFeel ---------------------------------------------
|
|
AmbivalenceLookAndFeel::AmbivalenceLookAndFeel()
|
|
{
|
|
setColour(juce::Slider::backgroundColourId, AmbivalenceColors::ArcTrack);
|
|
setColour(juce::Slider::thumbColourId, AmbivalenceColors::Accent);
|
|
setColour(juce::Slider::trackColourId, AmbivalenceColors::ArcFill);
|
|
setColour(juce::Label::textColourId, AmbivalenceColors::TextSecondary);
|
|
setColour(juce::ComboBox::backgroundColourId, AmbivalenceColors::Surface);
|
|
setColour(juce::ComboBox::textColourId, AmbivalenceColors::TextPrimary);
|
|
setColour(juce::ComboBox::outlineColourId, AmbivalenceColors::Border);
|
|
mainFont = juce::Font(juce::FontOptions("Helvetica Neue", 11.f, juce::Font::plain));
|
|
}
|
|
|
|
void AmbivalenceLookAndFeel::drawRotarySlider(juce::Graphics& g,
|
|
int x, int y, int w, int h,
|
|
float sliderPos, float startAngle, float endAngle, juce::Slider&)
|
|
{
|
|
auto b = juce::Rectangle<float>((float)x, (float)y, (float)w, (float)h).reduced(4.f);
|
|
float cx = b.getCentreX(), cy = b.getCentreY();
|
|
float r = juce::jmin(b.getWidth(), b.getHeight()) * 0.45f;
|
|
float th = r * 0.22f;
|
|
|
|
juce::Path track;
|
|
track.addCentredArc(cx, cy, r, r, 0.f, startAngle, endAngle, true);
|
|
g.setColour(AmbivalenceColors::ArcTrack);
|
|
g.strokePath(track, juce::PathStrokeType(th,
|
|
juce::PathStrokeType::curved, juce::PathStrokeType::rounded));
|
|
|
|
float angle = startAngle + sliderPos * (endAngle - startAngle);
|
|
juce::Path fill;
|
|
fill.addCentredArc(cx, cy, r, r, 0.f, startAngle, angle, true);
|
|
g.setColour(AmbivalenceColors::ArcFill);
|
|
g.strokePath(fill, juce::PathStrokeType(th,
|
|
juce::PathStrokeType::curved, juce::PathStrokeType::rounded));
|
|
|
|
g.setColour(AmbivalenceColors::Panel);
|
|
g.fillEllipse(cx - r * 0.28f, cy - r * 0.28f, r * 0.56f, r * 0.56f);
|
|
|
|
float ix = cx + r * 0.6f * std::sin(angle);
|
|
float iy = cy - r * 0.6f * std::cos(angle);
|
|
g.setColour(AmbivalenceColors::TextPrimary);
|
|
g.drawLine(cx, cy, ix, iy, 2.f);
|
|
}
|
|
|
|
void AmbivalenceLookAndFeel::drawLinearSlider(juce::Graphics& g,
|
|
int x, int y, int w, int h,
|
|
float sliderPos, float, float, juce::Slider::SliderStyle, juce::Slider&)
|
|
{
|
|
auto b = juce::Rectangle<int>(x, y, w, h).toFloat();
|
|
float ty = b.getCentreY() - 2.f;
|
|
g.setColour(AmbivalenceColors::ArcTrack);
|
|
g.fillRoundedRectangle(b.getX(), ty, b.getWidth(), 4.f, 2.f);
|
|
g.setColour(AmbivalenceColors::Accent);
|
|
g.fillRoundedRectangle(b.getX(), ty, sliderPos - b.getX(), 4.f, 2.f);
|
|
float r = 7.f;
|
|
g.setColour(AmbivalenceColors::TextPrimary);
|
|
g.fillEllipse(sliderPos - r, b.getCentreY() - r, r * 2.f, r * 2.f);
|
|
}
|
|
|
|
void AmbivalenceLookAndFeel::drawComboBox(juce::Graphics& g,
|
|
int w, int h, bool isDown, int, int, int, int, juce::ComboBox&)
|
|
{
|
|
auto b = juce::Rectangle<int>(0, 0, w, h).toFloat();
|
|
g.setColour(isDown ? AmbivalenceColors::Panel : AmbivalenceColors::Surface);
|
|
g.fillRoundedRectangle(b, 3.f);
|
|
g.setColour(AmbivalenceColors::Border);
|
|
g.drawRoundedRectangle(b.reduced(0.5f), 3.f, 1.f);
|
|
juce::Path arrow;
|
|
arrow.addTriangle(w - 16.f, h * 0.5f - 3.f,
|
|
w - 8.f, h * 0.5f - 3.f,
|
|
w - 12.f, h * 0.5f + 3.f);
|
|
g.setColour(AmbivalenceColors::TextSecondary);
|
|
g.fillPath(arrow);
|
|
}
|
|
|
|
void AmbivalenceLookAndFeel::positionComboBoxText(juce::ComboBox& box, juce::Label& label) {
|
|
label.setBounds(6, 1, box.getWidth() - 22, box.getHeight() - 2);
|
|
label.setFont(getComboBoxFont(box));
|
|
}
|
|
|
|
juce::Font AmbivalenceLookAndFeel::getLabelFont(juce::Label& label) { return label.getFont(); }
|
|
juce::Font AmbivalenceLookAndFeel::getComboBoxFont(juce::ComboBox&) { return mainFont.withHeight(11.f * zoomScale); }
|
|
|
|
void AmbivalenceLookAndFeel::drawGroupComponentOutline(juce::Graphics& g,
|
|
int w, int h, const juce::String& text,
|
|
const juce::Justification&, juce::GroupComponent&)
|
|
{
|
|
float textH = 12.f, indent = 8.f, yOff = textH * 0.5f;
|
|
juce::Path p;
|
|
p.startNewSubPath(indent + 4.f, yOff); p.lineTo(indent, yOff);
|
|
p.lineTo(indent, (float)h - 1.f);
|
|
p.lineTo((float)w - indent, (float)h - 1.f);
|
|
p.lineTo((float)w - indent, yOff);
|
|
|
|
// * redraw after changes
|
|
juce::GlyphArrangement ga;
|
|
ga.addLineOfText(mainFont.withHeight(textH), text, 0.f, 0.f);
|
|
float tw = ga.getBoundingBox(0, -1, true).getWidth() + 6.f;
|
|
|
|
p.lineTo(indent + 14.f + tw, yOff);
|
|
g.setColour(AmbivalenceColors::Border);
|
|
g.strokePath(p, juce::PathStrokeType(1.f));
|
|
g.setColour(AmbivalenceColors::TextSecondary);
|
|
g.setFont(mainFont.withHeight(textH).boldened());
|
|
g.drawText(text, (int)(indent + 14.f), 0, (int)tw, (int)textH,
|
|
juce::Justification::centredLeft);
|
|
}
|
|
|
|
// --- VUMeter ---------------------------------------------------------
|
|
VUMeter::VUMeter(const juce::String& lbl, Side s) : label(lbl), side(s) {}
|
|
|
|
void VUMeter::paint(juce::Graphics& g)
|
|
{
|
|
auto b = getLocalBounds().toFloat().reduced(1.f);
|
|
g.setColour(AmbivalenceColors::Surface);
|
|
g.fillRoundedRectangle(b, 3.f);
|
|
|
|
const float inset = 8.f;
|
|
const float labelW = 20.f;
|
|
const float bx = b.getX() + inset + labelW + 2.f;
|
|
const float bw = b.getWidth() - (bx - b.getX()) - inset;
|
|
|
|
const float barH = 7.f;
|
|
const float gap = 2.f;
|
|
const float blockH = barH * 2.f + gap;
|
|
const float startY = b.getY() + (b.getHeight() - blockH) * 0.5f;
|
|
|
|
auto bar = [&](float y, float level) {
|
|
float n = juce::jlimit(0.f, 1.f, juce::jmap(
|
|
juce::Decibels::gainToDecibels(level + 1e-9f), -60.f, 0.f, 0.f, 1.f));
|
|
g.setColour(AmbivalenceColors::ArcTrack);
|
|
g.fillRoundedRectangle(bx, y, bw, barH, 2.f);
|
|
juce::ColourGradient gr(AmbivalenceColors::AccentBlue, bx, y,
|
|
AmbivalenceColors::Accent, bx + bw, y, false);
|
|
g.setGradientFill(gr);
|
|
g.fillRoundedRectangle(bx, y, bw * n, barH, 2.f);
|
|
};
|
|
bar(startY, levelL);
|
|
bar(startY + barH + gap, levelR);
|
|
|
|
g.setColour(AmbivalenceColors::TextSecondary);
|
|
g.setFont(8.f * zoomScale);
|
|
g.drawText(label, (int)(b.getX() + inset), (int)b.getY(), (int)labelW,
|
|
(int)b.getHeight(), juce::Justification::centredLeft);
|
|
}
|
|
|
|
// --- ArcKnob ---------------------------------------------------------
|
|
void ArcKnob::build(juce::AudioProcessorValueTreeState& apvts,
|
|
const juce::String& paramID,
|
|
const juce::String& labelText,
|
|
juce::Component* parent,
|
|
AmbivalenceLookAndFeel& laf)
|
|
{
|
|
slider.setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
|
|
slider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 62, 14);
|
|
slider.setLookAndFeel(&laf);
|
|
slider.setColour(juce::Slider::textBoxTextColourId,
|
|
AmbivalenceColors::TextSecondary);
|
|
slider.setColour(juce::Slider::textBoxOutlineColourId,
|
|
juce::Colours::transparentBlack);
|
|
parent->addAndMakeVisible(slider);
|
|
|
|
label.setText(labelText, juce::dontSendNotification);
|
|
label.setJustificationType(juce::Justification::centred);
|
|
label.setFont(juce::Font(juce::FontOptions(9.f)));
|
|
label.setColour(juce::Label::textColourId, AmbivalenceColors::TextSecondary);
|
|
parent->addAndMakeVisible(label);
|
|
|
|
// * after changes
|
|
attachment.reset(
|
|
new juce::AudioProcessorValueTreeState::SliderAttachment(
|
|
apvts, paramID, slider));
|
|
} |