Files
serumalt/Source/GUI/SerumLookAndFeel.h
T

157 lines
6.9 KiB
C++

#pragma once
#include <JuceHeader.h>
#include "../Resources.h"
namespace serum
{
// ===========================================================================
// Dark vector look-and-feel. Rotary and linear sliders are drawn with juce::Path
// (arcs + indicator + thumb) so they stay crisp at every UI scale.
// ===========================================================================
class SerumLookAndFeel : public juce::LookAndFeel_V4
{
public:
SerumLookAndFeel()
{
setColour (juce::Slider::backgroundColourId, theme::panel);
setColour (juce::Slider::thumbColourId, theme::accent);
setColour (juce::Slider::trackColourId, theme::outline);
setColour (juce::Slider::rotarySliderFillColourId, theme::accent);
setColour (juce::Slider::rotarySliderOutlineColourId, theme::panelRaised);
setColour (juce::Slider::textBoxTextColourId, theme::text);
setColour (juce::Slider::textBoxBackgroundColourId, theme::panel);
setColour (juce::Slider::textBoxHighlightColourId, theme::accent);
setColour (juce::Slider::textBoxOutlineColourId, theme::outline);
setColour (juce::ComboBox::backgroundColourId, theme::panelRaised);
setColour (juce::ComboBox::textColourId, theme::text);
setColour (juce::ComboBox::arrowColourId, theme::textDim);
setColour (juce::ComboBox::outlineColourId, theme::outline);
setColour (juce::ComboBox::buttonColourId, theme::panelRaised);
setColour (juce::ComboBox::focusedOutlineColourId, theme::accent);
setColour (juce::PopupMenu::backgroundColourId, theme::panelRaised);
setColour (juce::PopupMenu::textColourId, theme::text);
setColour (juce::PopupMenu::highlightedBackgroundColourId, theme::accent);
setColour (juce::PopupMenu::highlightedTextColourId, juce::Colours::black);
setColour (juce::TextButton::buttonColourId, theme::panelRaised);
setColour (juce::TextButton::buttonOnColourId, theme::accent);
setColour (juce::TextButton::textColourOffId, theme::textDim);
setColour (juce::TextButton::textColourOnId, juce::Colours::black);
setColour (juce::TextEditor::backgroundColourId, theme::panel);
setColour (juce::TextEditor::textColourId, theme::text);
setColour (juce::TextEditor::outlineColourId, theme::outline);
setColour (juce::TextEditor::focusedOutlineColourId, theme::accent);
}
void drawRotarySlider (juce::Graphics& g, int x, int y, int width, int height,
float sliderPos, float, float, juce::Slider& slider) override
{
const int labelH = 18;
const int knobH = height - labelH;
const int knobSize = juce::jmin (width, knobH) - 6;
const int cx = x + width / 2;
const int cy = y + knobH / 2;
const float radius = knobSize * 0.5f;
const juce::Rectangle<float> area ((float) cx - radius, (float) cy - radius,
radius * 2.0f, radius * 2.0f);
// Body.
g.setColour (theme::panelRaised);
g.fillEllipse (area);
g.setColour (theme::outline);
g.drawEllipse (area, 1.0f);
const float startAngle = juce::MathConstants<float>::pi * 1.25f; // 225 deg
const float endAngle = juce::MathConstants<float>::pi * -0.25f;
const float valueAngle = startAngle + sliderPos * (endAngle - startAngle);
// Track arc.
juce::Path track;
track.addCentredArc ((float) cx, (float) cy, radius - 4.0f, radius - 4.0f,
0.0f, startAngle, endAngle, true);
g.setColour (theme::outline);
g.strokePath (track, juce::PathStrokeType (3.0f, juce::PathStrokeType::curved));
// Value arc.
juce::Path valueArc;
valueArc.addCentredArc ((float) cx, (float) cy, radius - 4.0f, radius - 4.0f,
0.0f, startAngle, valueAngle, true);
g.setColour (theme::accent);
g.strokePath (valueArc, juce::PathStrokeType (3.0f, juce::PathStrokeType::curved));
// Indicator.
const float indX = (float) cx + (radius - 9.0f) * std::cos (valueAngle);
const float indY = (float) cy + (radius - 9.0f) * std::sin (valueAngle);
g.setColour (theme::text);
g.drawLine ((float) cx, (float) cy, indX, indY, 2.0f);
g.setColour (theme::text);
g.fillEllipse (indX - 2.0f, indY - 2.0f, 4.0f, 4.0f);
// Value readout.
g.setColour (theme::text);
g.setFont (juce::Font (10.0f, juce::Font::bold));
g.drawText (slider.getTextFromValue (slider.getValue()),
juce::Rectangle<int> (x, y, width, knobH - (int) radius + 8),
juce::Justification::centred, false);
// Name label.
g.setColour (theme::textDim);
g.setFont (juce::Font (11.0f));
g.drawText (slider.getName(), juce::Rectangle<int> (x, y + knobH, width, labelH),
juce::Justification::centred, false);
}
void drawLinearSlider (juce::Graphics& g, int x, int y, int width, int height,
float sliderPos, float, float, juce::Slider::SliderStyle, juce::Slider& slider) override
{
const bool vertical = height > width;
juce::Rectangle<int> track;
if (vertical)
{
track = juce::Rectangle<int> (x + width / 2 - 3, y + 8, 6, height - 16);
}
else
{
track = juce::Rectangle<int> (x + 8, y + height / 2 - 3, width - 16, 6);
}
g.setColour (theme::outline);
g.fillRoundedRectangle (track.toFloat(), 3.0f);
juce::Rectangle<float> fill;
if (vertical)
{
const float h = track.getHeight() * sliderPos;
fill = juce::Rectangle<float> ((float) track.getX(), (float) track.getBottom() - h,
(float) track.getWidth(), h);
}
else
{
fill = juce::Rectangle<float> ((float) track.getX(), (float) track.getY(),
track.getWidth() * sliderPos, (float) track.getHeight());
}
g.setColour (theme::accent);
g.fillRoundedRectangle (fill, 3.0f);
// Thumb.
const float thumbC = vertical ? (track.getBottom() - track.getHeight() * sliderPos)
: (track.getX() + track.getWidth() * sliderPos);
juce::Point<float> thumb (vertical ? (float) track.getCentreX() : thumbC,
vertical ? thumbC : (float) track.getCentreY());
g.setColour (theme::text);
g.fillEllipse (thumb.x - 6.0f, thumb.y - 6.0f, 12.0f, 12.0f);
g.setColour (theme::textDim);
g.setFont (juce::Font (10.0f));
g.drawText (slider.getName(), x, y, width, height, juce::Justification::bottomLeft, false);
}
};
} // namespace serum