JUCEのSlider（の見た目）をカスタマイズする

##仕上がりイメージ
Rotary Sliderだけ変更して、こんな感じの見た目にする。針ではなく数値表示。大きさや色はProjucerで設計時に変更できるように。
![Simulator Screen Shot - iPhone 5s - 2019-01-06 at 10.47.56.png](https://qiita-image-store.s3.amazonaws.com/0/107818/9180285d-a423-834b-5c06-988367e3f35c.png)


##LookAndFeelをオーバーライド
コード的な変更は、独自のLookAndFeelクラスを作って描画を変更したい部分だけメソッドをオーバーライドする。この例ではLookAndFeel_V4をベースに、LookAndFeel_V2のコードをJUCE本体からコピペして改変してる。

```MyLookAndFeel.h
#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

class IfwLookAndFeel : public LookAndFeel_V4
{
public:
    IfwLookAndFeel() {}
    ~IfwLookAndFeel() {}
    
    void drawRotarySlider (Graphics& g, int x, int y, int width, int height,
                           float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle,
                           Slider&) override;
};
```

```MyLookAndFeel.cpp
#include "IfwLookAndFeel.h"


void IfwLookAndFeel::drawRotarySlider (Graphics& g, int x, int y, int width, int height,
                       float sliderPos, float rotaryStartAngle, float rotaryEndAngle,
                       Slider& slider)
{
    const float radius = jmin (width / 2, height / 2) - 2.0f;
    const float centreX = x + width * 0.5f;
    const float centreY = y + height * 0.5f;
    const float rx = centreX - radius;
    const float ry = centreY - radius;
    const float rw = radius * 2.0f;
    const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
    const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled();
    
    if (radius > 0.1f)  // was 12.0f)
    {
        if (slider.isEnabled())
            //g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
            g.setColour (slider.findColour (Slider::thumbColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
        else
            g.setColour (Colour (0x80808080));
        
        const float thickness = isMouseOver? 0.8f : 0.85f;  //  was 0.7f;
        
        {
            Path filledArc;
            filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, angle, thickness);
            g.fillPath (filledArc);
        }
        
        {
            /* Draw value text instead of needle
             const float innerRadius = radius * 0.2f;
             Path p;
             p.addTriangle (-innerRadius, 0.0f,
             0.0f, -radius * thickness * 1.1f,
             innerRadius, 0.0f);
             
             p.addEllipse (-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f);
             
             g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY));
            */
            //g.setColour(Colours::white);
            String valueText = slider.getTextFromValue(slider.getValue());
            int textWidth  = (int)(((float)width) * 0.55f);
            int textHeight = (int)(((float)height) * 0.25f);
            int textX = (width - textWidth) / 2;
            int textY = (height - textHeight) / 2;

            //g.setColour (slider.findColour (Slider::textBoxBackgroundColourId));
            g.setColour (Colour(0x40000000));
            g.fillEllipse(x, y, width, height);
            g.setFont (Font (slider.getTextBoxHeight(), Font::plain).withTypefaceStyle ("Regular"));
            g.setColour (slider.findColour (Slider::textBoxTextColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
            g.drawText(valueText, textX, textY, textWidth, textHeight, Justification::centred);
        }
        
        if (slider.isEnabled())
            g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId));
        else
            g.setColour (Colour (0x80808080));
        
        Path outlineArc;
        outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness);
        outlineArc.closeSubPath();
        
        g.strokePath (outlineArc, PathStrokeType (slider.isEnabled() ? (isMouseOver ? 2.0f : 1.2f) : 0.3f));
        //auto lineW = jmin (8.0f, radius * 0.5f);
        //g.strokePath (outlineArc, PathStrokeType (lineW, PathStrokeType::curved, PathStrokeType::rounded));
    }
    else
    {
        if (slider.isEnabled())
            //g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
            g.setColour (slider.findColour (Slider::thumbColourId).withAlpha (isMouseOver ? 1.0f : 0.7f));
        else
            g.setColour (Colour (0x80808080));
        
        Path p;
        p.addEllipse (-0.4f * rw, -0.4f * rw, rw * 0.8f, rw * 0.8f);
        PathStrokeType (rw * 0.1f).createStrokedPath (p, p);
        
        p.addLineSegment (Line<float> (0.0f, 0.0f, 0.0f, -radius), rw * 0.2f);
        
        g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY));
    }
}
```

##カスタマイズしたProjucerを作成
同じ変更をProjucer > Utility > UI > Jucer_ProjucerLookAndFeel.h/cppにも追加してProjucerをビルドすると、Projucer上でも見た目を変えられる。カスタマイズしたLookAndFeelで色はthumbを、フォントサイズはtext box heightを使うようにしてあるので、Projucer上で変更できて便利。フォントサイズをSliderの大きさに合わせて自動変更にしていないのは意図的なもの（用途によってそうしたければすればよい）。
<img width="1264" alt="スクリーンショット 2019-01-06 10.58.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/107818/c597b6be-b6a7-ca42-91b4-fda19e66d879.png">