12        : deathMetalFont(juce::FontOptions(
 
   13              juce::Typeface::createSystemTypefaceFor(CustomFont::ArtDystopia_ttf, CustomFont::ArtDystopia_ttfSize)))
 
   20    juce::Font getLabelFont(juce::Label &label)
 override 
   23        return deathMetalFont;
 
   26    juce::Font getPopupMenuFont()
 override 
   28        return deathMetalFont;
 
   31    void drawButtonText(juce::Graphics &g, juce::TextButton &button, 
bool isMouseOverButton, 
bool isButtonDown)
 override 
   33        std::ignore = isMouseOverButton;
 
   34        std::ignore = isButtonDown;
 
   35        auto font = deathMetalFont;
 
   37        g.setColour(button.findColour(juce::TextButton::textColourOnId));
 
   38        g.drawText(button.getButtonText(), button.getLocalBounds(), juce::Justification::centred, 
true);
 
   40        button.setColour(juce::TextButton::ColourIds::buttonColourId, juce::Colours::black);
 
   41        button.setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::black);
 
   44    void drawLinearSlider(juce::Graphics &g, 
int x, 
int y, 
int width, 
int height, 
float sliderPos, 
float minSliderPos,
 
   45                          float maxSliderPos, juce::Slider::SliderStyle style, juce::Slider &slider)
 override 
   49            g.setColour(slider.findColour(juce::Slider::trackColourId));
 
   50            g.fillRect(slider.isHorizontal() ? juce::Rectangle<float>(
static_cast<float>(x), (
float)y + 0.5f,
 
   51                                                                      sliderPos - (
float)x, (
float)height - 1.0f)
 
   52                                             : juce::Rectangle<float>((
float)x + 0.5f, sliderPos, (
float)width - 1.0f,
 
   53                                                                      (
float)y + ((
float)height - sliderPos)));
 
   55            drawLinearSliderOutline(g, x, y, width, height, style, slider);
 
   59            auto isTwoVal = (style == juce::Slider::SliderStyle::TwoValueVertical ||
 
   60                             style == juce::Slider::SliderStyle::TwoValueHorizontal);
 
   61            auto isThreeVal = (style == juce::Slider::SliderStyle::ThreeValueVertical ||
 
   62                               style == juce::Slider::SliderStyle::ThreeValueHorizontal);
 
   64            auto trackWidth = juce::jmin(6.0f, slider.isHorizontal() ? (
float)height * 0.25f : (
float)width * 0.25f);
 
   66            Point<float> startPoint(slider.isHorizontal() ? (
float)x : (
float)x + (
float)width * 0.5f,
 
   67                                    slider.isHorizontal() ? (
float)y + (
float)height * 0.5f : (
float)(height + y));
 
   69            Point<float> endPoint(slider.isHorizontal() ? (
float)(width + x) : startPoint.x,
 
   70                                  slider.isHorizontal() ? startPoint.y : (
float)y);
 
   72            juce::Path backgroundTrack;
 
   73            backgroundTrack.startNewSubPath(startPoint);
 
   74            backgroundTrack.lineTo(endPoint);
 
   75            g.setColour(slider.findColour(juce::Slider::backgroundColourId));
 
   76            g.strokePath(backgroundTrack, {trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded});
 
   78            juce::Path valueTrack;
 
   79            juce::Point<float> minPoint, maxPoint, thumbPoint;
 
   81            if (isTwoVal || isThreeVal)
 
   83                minPoint = {slider.isHorizontal() ? minSliderPos : (float)width * 0.5f,
 
   84                            slider.isHorizontal() ? (float)height * 0.5f : minSliderPos};
 
   87                    thumbPoint = {slider.isHorizontal() ? sliderPos : (float)width * 0.5f,
 
   88                                  slider.isHorizontal() ? (float)height * 0.5f : sliderPos};
 
   90                maxPoint = {slider.isHorizontal() ? maxSliderPos : (float)width * 0.5f,
 
   91                            slider.isHorizontal() ? (float)height * 0.5f : maxSliderPos};
 
   95                auto kx = slider.isHorizontal() ? sliderPos : ((float)x + (float)width * 0.5f);
 
   96                auto ky = slider.isHorizontal() ? ((float)y + (float)height * 0.5f) : sliderPos;
 
   98                minPoint = startPoint;
 
  102            auto thumbWidth = getSliderThumbRadius(slider) + 4;
 
  104            valueTrack.startNewSubPath(minPoint);
 
  105            valueTrack.lineTo(isThreeVal ? thumbPoint : maxPoint);
 
  106            g.setColour(slider.findColour(juce::Slider::trackColourId));
 
  107            g.strokePath(valueTrack, {trackWidth, juce::PathStrokeType::curved, juce::PathStrokeType::rounded});
 
  111                g.setColour(juce::Colours::black);
 
  112                g.fillEllipse(juce::Rectangle<float>(
static_cast<float>(thumbWidth), 
static_cast<float>(thumbWidth))
 
  113                                  .withCentre(isThreeVal ? thumbPoint : maxPoint));
 
  115                g.setColour(juce::Colours::white);
 
  117                juce::Point<float> point1(maxPoint.getX(), maxPoint.getY());
 
  118                point1.setX(point1.getX() + (thumbWidth / 2) * ::sinf(0.f * std::numbers::pi / 180.f));
 
  119                point1.setY(point1.getY() + (thumbWidth / 2) * ::cosf(0.f * std::numbers::pi / 180.f));
 
  121                juce::Point<float> point2(maxPoint.getX(), maxPoint.getY());
 
  122                point2.setX(point2.getX() +
 
  123                            (thumbWidth / 2) * ::sinf(144.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  124                point2.setY(point2.getY() +
 
  125                            (thumbWidth / 2) * ::cosf(144.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  127                juce::Point<float> point3(maxPoint.getX(), maxPoint.getY());
 
  128                point3.setX(point3.getX() +
 
  129                            (thumbWidth / 2) * ::sinf(288.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  130                point3.setY(point3.getY() +
 
  131                            (thumbWidth / 2) * ::cosf(288.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  133                juce::Point<float> point4(maxPoint.getX(), maxPoint.getY());
 
  134                point4.setX(point4.getX() +
 
  135                            (thumbWidth / 2) * ::sinf(72.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  136                point4.setY(point4.getY() +
 
  137                            (thumbWidth / 2) * ::cosf(72.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  139                juce::Point<float> point5(maxPoint.getX(), maxPoint.getY());
 
  140                point5.setX(point5.getX() +
 
  141                            (thumbWidth / 2) * ::sinf(216.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  142                point5.setY(point5.getY() +
 
  143                            (thumbWidth / 2) * ::cosf(216.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  145                juce::Path pentagramPath;
 
  146                pentagramPath.startNewSubPath(point1);
 
  147                pentagramPath.lineTo(point2);
 
  148                pentagramPath.lineTo(point3);
 
  149                pentagramPath.lineTo(point4);
 
  150                pentagramPath.lineTo(point5);
 
  151                pentagramPath.closeSubPath();
 
  152                g.strokePath(pentagramPath, juce::PathStrokeType(1));
 
  154                g.drawEllipse(juce::Rectangle<float>(
static_cast<float>(thumbWidth), 
static_cast<float>(thumbWidth))
 
  155                                  .withCentre(isThreeVal ? thumbPoint : maxPoint)
 
  160            if (isTwoVal || isThreeVal)
 
  162                auto sr = juce::jmin(trackWidth, (slider.isHorizontal() ? (
float)height : (
float)width) * 0.4f);
 
  163                auto pointerColour = slider.findColour(juce::Slider::thumbColourId);
 
  165                if (slider.isHorizontal())
 
  167                    drawPointer(g, minSliderPos - sr,
 
  168                                juce::jmax(0.0f, (
float)y + (
float)height * 0.5f - trackWidth * 2.0f),
 
  169                                trackWidth * 2.0f, pointerColour, 2);
 
  171                    drawPointer(g, maxSliderPos - trackWidth,
 
  172                                juce::jmin((
float)(y + height) - trackWidth * 2.0f, (
float)y + (
float)height * 0.5f),
 
  173                                trackWidth * 2.0f, pointerColour, 4);
 
  177                    drawPointer(g, juce::jmax(0.0f, (
float)x + (
float)width * 0.5f - trackWidth * 2.0f),
 
  178                                minSliderPos - trackWidth, trackWidth * 2.0f, pointerColour, 1);
 
  180                    drawPointer(g, juce::jmin((
float)(x + width) - trackWidth * 2.0f, (
float)x + (
float)width * 0.5f),
 
  181                                maxSliderPos - sr, trackWidth * 2.0f, pointerColour, 3);
 
  186                drawLinearSliderOutline(g, x, y, width, height, style, slider);
 
  190    void drawRotarySlider(juce::Graphics &g, 
int x, 
int y, 
int width, 
int height, 
float sliderPosProportional,
 
  191                          float rotaryStartAngle, 
float rotaryEndAngle, juce::Slider &slider)
 override 
  193        std::ignore = slider;
 
  194        auto radius = (float)juce::jmin(width / 2, height / 2) - 4.0f;
 
  195        auto centreX = (float)x + (
float)width * 0.5f;
 
  196        auto centreY = (float)y + (
float)height * 0.5f;
 
  197        auto rx = centreX - radius;
 
  198        auto ry = centreY - radius;
 
  199        auto rw = radius * 2.0f;
 
  200        auto angle = rotaryStartAngle + sliderPosProportional * (rotaryEndAngle - rotaryStartAngle);
 
  202        auto lineW = radius * 0.135f;
 
  204        juce::Rectangle<float> bounds(rx, ry, rw, rw);
 
  207        g.setColour(juce::Colours::black);
 
  208        g.fillEllipse(bounds);
 
  211        float adjustedRadius = radius - (lineW / 2.0f);
 
  213        juce::Path backgroundArc;
 
  214        backgroundArc.addCentredArc(centreX, centreY, adjustedRadius, adjustedRadius, 0.0f, rotaryStartAngle,
 
  215                                    rotaryEndAngle, 
true);
 
  217        g.setColour(juce::Colours::grey.darker(1.0));
 
  218        g.strokePath(backgroundArc,
 
  219                     juce::PathStrokeType(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded));
 
  223        arcPath.addCentredArc(centreX, centreY, adjustedRadius, adjustedRadius, 0.0f, rotaryStartAngle, angle, 
true);
 
  226        juce::Image offscreenImage(juce::Image::ARGB, width, height, 
true);
 
  228            juce::Graphics offscreenGraphics(offscreenImage);
 
  231            offscreenGraphics.fillAll(juce::Colours::transparentBlack);
 
  234            offscreenGraphics.setColour(juce::Colours::white);
 
  236            offscreenGraphics.strokePath(
 
  237                arcPath, juce::PathStrokeType(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded));
 
  244        juce::Image unboundImage = offscreenImage.createCopy();
 
  247        juce::Image glowImage(juce::Image::ARGB, width, height, 
true);
 
  249            juce::Graphics glowGraphics(glowImage);
 
  250            juce::GlowEffect glowEffect;
 
  251            glowEffect.setGlowProperties(5.0f, juce::Colours::yellow);
 
  252            glowEffect.applyEffect(unboundImage, glowGraphics, 1.0f, 1.0f);
 
  256        g.drawImageAt(glowImage, x, y);
 
  258        g.setColour(juce::Colours::white);
 
  260        juce::Point<float> point1(centreX, centreY);
 
  261        point1.setX(point1.getX() + (radius - lineW * 1.5f) * ::sinf(0.f * std::numbers::pi / 180.f));
 
  262        point1.setY(point1.getY() + (radius - lineW * 1.5f) * ::cosf(0.f * std::numbers::pi / 180.f));
 
  264        juce::Point<float> point2(centreX, centreY);
 
  265        point2.setX(point2.getX() +
 
  266                    (radius - lineW * 1.5f) * ::sinf(144.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  267        point2.setY(point2.getY() +
 
  268                    (radius - lineW * 1.5f) * ::cosf(144.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  270        juce::Point<float> point3(centreX, centreY);
 
  271        point3.setX(point3.getX() +
 
  272                    (radius - lineW * 1.5f) * ::sinf(288.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  273        point3.setY(point3.getY() +
 
  274                    (radius - lineW * 1.5f) * ::cosf(288.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  276        juce::Point<float> point4(centreX, centreY);
 
  277        point4.setX(point4.getX() +
 
  278                    (radius - lineW * 1.5f) * ::sinf(72.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  279        point4.setY(point4.getY() +
 
  280                    (radius - lineW * 1.5f) * ::cosf(72.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  282        juce::Point<float> point5(centreX, centreY);
 
  283        point5.setX(point5.getX() +
 
  284                    (radius - lineW * 1.5f) * ::sinf(216.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  285        point5.setY(point5.getY() +
 
  286                    (radius - lineW * 1.5f) * ::cosf(216.f * 
static_cast<float>(std::numbers::pi) / 180.f));
 
  288        juce::Path pentagramPath;
 
  289        pentagramPath.startNewSubPath(point1);
 
  290        pentagramPath.lineTo(point2);
 
  291        pentagramPath.lineTo(point3);
 
  292        pentagramPath.lineTo(point4);
 
  293        pentagramPath.lineTo(point5);
 
  294        pentagramPath.closeSubPath();
 
  297        juce::AffineTransform transform =
 
  298            juce::AffineTransform::translation(-centreX, -centreY).rotated(angle).translated(centreX, centreY);
 
  299        pentagramPath.applyTransform(transform);
 
  301        g.strokePath(pentagramPath, juce::PathStrokeType(2));
 
  304        g.drawEllipse(bounds.reduced(lineW * 2.f), 2.f);
 
  307        juce::Path crossPath;
 
  310        crossPath.addRectangle(centreX - lineW / 2, centreY + radius / 6, lineW, -radius);
 
  314        crossPath.addRectangle(centreX - (radius / 3 / 2), centreY - radius / 10, radius / 3, lineW);
 
  318        crossPath.applyTransform(transform);
 
  320        g.setColour(juce::Colours::white);
 
  321        g.fillPath(crossPath);
 
  324    void drawGroupComponentOutline(juce::Graphics &g, 
int width, 
int height, 
const juce::String &text,
 
  325                                   const juce::Justification &position, juce::GroupComponent &group)
 override 
  327        const float textH = 15.0f;
 
  328        const float indent = 3.0f;
 
  329        const float textEdgeGap = 4.0f;
 
  334        auto y = deathMetalFont.getAscent() - 3.0f;
 
  335        auto w = juce::jmax(0.0f, (
float)width - x * 2.0f);
 
  336        auto h = juce::jmax(0.0f, (
float)height - y - indent);
 
  337        cs = juce::jmin(cs, w * 0.5f, h * 0.5f);
 
  338        auto cs2 = 2.0f * cs;
 
  340        auto textW = text.isEmpty() ? 0 : juce::jlimit(0.0f, juce::jmax(0.0f, w - cs2 - textEdgeGap * 2), [&]() {
 
  342            juce::TextLayout layout;
 
  343            juce::AttributedString attributedText;
 
  344            attributedText.append(text, deathMetalFont, juce::Colours::black); 
 
  346            layout.createLayout(attributedText, w - cs2 - textEdgeGap * 2);
 
  347            return layout.getWidth() + textEdgeGap * 2.0f;
 
  349        auto textX = cs + textEdgeGap;
 
  351        if (position.testFlags(juce::Justification::horizontallyCentred))
 
  352            textX = cs + (w - cs2 - textW) * 0.5f;
 
  353        else if (position.testFlags(juce::Justification::right))
 
  354            textX = w - cs - textW - textEdgeGap;
 
  356        p.startNewSubPath(x + textX + textW, y);
 
  357        p.lineTo(x + w - cs, y);
 
  359        p.addArc(x + w - cs2, y, cs2, cs2, 0, juce::MathConstants<float>::halfPi);
 
  360        p.lineTo(x + w, y + h - cs);
 
  362        p.addArc(x + w - cs2, y + h - cs2, cs2, cs2, juce::MathConstants<float>::halfPi,
 
  363                 juce::MathConstants<float>::pi);
 
  364        p.lineTo(x + cs, y + h);
 
  366        p.addArc(x, y + h - cs2, cs2, cs2, juce::MathConstants<float>::pi, juce::MathConstants<float>::pi * 1.5f);
 
  369        p.addArc(x, y, cs2, cs2, juce::MathConstants<float>::pi * 1.5f, juce::MathConstants<float>::twoPi);
 
  370        p.lineTo(x + textX, y);
 
  372        auto alpha = group.isEnabled() ? 1.0f : 0.5f;
 
  374        g.setColour(group.findColour(juce::GroupComponent::outlineColourId).withMultipliedAlpha(alpha));
 
  376        g.strokePath(p, juce::PathStrokeType(2.0f));
 
  378        g.setColour(group.findColour(juce::GroupComponent::textColourId).withMultipliedAlpha(alpha));
 
  379        g.setFont(deathMetalFont);
 
  380        g.drawText(text, juce::roundToInt(x + textX), 0, juce::roundToInt(textW), juce::roundToInt(textH),
 
  381                   juce::Justification::centred, 
true);
 
  384    void drawPopupMenuBackground(juce::Graphics &g, 
int width, 
int height)
 override 
  386        std::ignore = height;
 
  388        g.fillAll(juce::Colours::black); 
 
  392    juce::Font deathMetalFont;
 
  393    juce::ColourGradient mGradient;