ボタンもカスタマイズできます。押した時と押していない時のグラフィックもgraphicsクラスの描画で色々とできるみたいです。
ボタンの描画の時にはぜひ使いたいね!
「Interface Design」の「Customise the look and feel of your app」チュートリアル、「Button customisation」の項目を進めていきます。ボタンの見た目をカスタマイズします。また、クリックしたときのボタンの挙動もカスタマイズするなど、汎用性が高そうな内容となっています。
公式のチュートリアルページはこちらになります。
こんな人の役に立つかも
・JUCEプログラミングを勉強している人
・JUCEチュートリアル「Customise the look and feel of your app」をやっている人
・JUCEのボタンをカスタマイズしたい人
Button customisation
プログラムの準備
まずは、前回の記事までで作成した「OtherLookAndFeel」クラスをMainComponentクラスのlookAndFeelとして適用させて、ダイヤルのデザインをデフォルトの状態でカスタマイズした状態にします。setLookAndFeel関数をコンストラクタで呼び出して、MainComponentのlookAndFeelに設定する変更をおこないました。
MainComponent::MainComponent()
{
//...略...
//dial1.setLookAndFeel(&otherLookAndFeel);//コメントアウトしました。
//以下のようにMainComponentクラスのsetLookAndFeel関数で設定します。
setLookAndFeel(&otherLookAndFeel);
setSize(300, 200);
}
デストラクタのnullptrを設定する部分も、次のように変更しました。
MainComponent::~MainComponent()
{
//dial1.setLookAndFeel(nullptr);//コメントアウトしました。
setLookAndFeel(nullptr);
}
ここまでをベースに、次はボタンのカスタマイズを行います。
ボタンのカスタマイズを行うと、アプリが次のように、ボタンの押下時と通常時のグラフィックで変化するようになります。
ボタン背景の描画
OtherLookAnfFeelクラスに、drawButtonBackground関数をオーバーライドして定義します。ここで、次のようにボタンの背景を描画する処理をおこなうことで、ボタンの背景を塗りつぶします。
void drawButtonBackground(juce::Graphics& g, juce::Button& button, const juce::Colour& backgroundColour,
bool, bool isButtonDown) override
{
auto buttonArea = button.getLocalBounds();
g.setColour(backgroundColour);
g.fillRect(buttonArea);
}
getLocalBounds関数でボタンの矩形領域を取得し、引数として受け取る、backgroundColour色に描画色を設定します。fillRectで塗りつぶし矩形を描画することで、ボタンの背景色を表現しています。
押し下げ時の背景追加
これだけでは、静的な表現になってしまうので、ボタンがクリックされたときに動きを付けます。
void drawButtonBackground(juce::Graphics& g, juce::Button& button, const juce::Colour& backgroundColour,
bool, bool isButtonDown) override
{
auto buttonArea = button.getLocalBounds();
//[1]立体感を出すための背景です。
auto edge = 4;
buttonArea.removeFromLeft(edge);
buttonArea.removeFromTop(edge);
g.setColour(juce::Colours::darkgrey.withAlpha(0.5f));
g.fillRect(buttonArea);
//[2]押し下げ字のボタン背景描画位置設定です。
auto offset = isButtonDown ? -edge / 2 : -edge;
buttonArea.translate(offset, offset);
g.setColour(backgroundColour);
g.fillRect(buttonArea);
}
[1]で、removeFromLeftにより、buttonAreaの矩形領域を左から4小さい領域として取得します。同じように、removeFromTopで矩形領域をさらに4小さい下部の矩形領域として取得します。最終的にbuttonAreaの矩形領域は左と上に4の余白を開けたような矩形領域となります。ここを0.5の透明度でdarkgrayで塗りつぶします。
[2]では、ボタンが押されているとTrueとなる「isButtonDown」引数を利用して、offsetの値を取得します。押されているときはoffsetが-2、押されていないときはoffsetが-4という数値になります。そして、buttonAreaの矩形領域をoffset分XとY方向にtranslate関数で移動させています。これで、先ほどの矩形領域が矩形サイズはそのままに、原点は左上へ移動した形になります。ボタンをクリックしている時の描画はクリックしていないときよりも右下へ矩形が移動しますので、ボタンを押し下げたようなグラフィックになります。
テキストの描画
現状では、ボタンのテキストは動きませんので、別途「drawButtonText」関数をオーバーライドして押し下げの動きに追従するようにします。
以下のプログラムを「OtherLookAndFeel」クラスに追加します。
void drawButtonText(juce::Graphics& g, juce::TextButton& button, bool, bool isButtonDown) override
{
//[1]フォントを取得します。
auto font = getTextButtonFont(button, button.getHeight());
g.setFont(font);
//[2]トグルボタンのとき、テキスト色を変更する処理です。
g.setColour(button.findColour(button.getToggleState() ? juce::TextButton::textColourOnId
: juce::TextButton::textColourOffId)
.withMultipliedAlpha(button.isEnabled() ? 1.0f : 0.5f));
//[3]最後の描画で使用するパラメータ作成です。
auto yIndent = juce::jmin(4, button.proportionOfHeight(0.3f));
auto cornerSize = juce::jmin(button.getHeight(), button.getWidth()) / 2;
auto fontHeight = juce::roundToInt(font.getHeight() * 0.6f);
auto leftIndent = juce::jmin(fontHeight, 2 + cornerSize / (button.isConnectedOnLeft() ? 4 : 2));
auto rightIndent = juce::jmin(fontHeight, 2 + cornerSize / (button.isConnectedOnRight() ? 4 : 2));
auto textWidth = button.getWidth() - leftIndent - rightIndent;
auto edge = 4;
auto offset = isButtonDown ? edge / 2 : 0;
//[4]テキストの描画です。
if (textWidth > 0)
g.drawFittedText(button.getButtonText(),
leftIndent + offset, yIndent + offset, textWidth, button.getHeight() - yIndent * 2 - edge,
juce::Justification::centred, 2);
}
[1]では、フォントを取得します。font変数にFontクラスのオブジェクトを取得します。そして、これから描画するgraphicsオブジェクトのフォントをsetFontで設定します。
[2]では、ボタンがトグルボタンのとき、取得したボタンの状態によって、テキスト色を変更します。getToggleState関数で、TrueのときはtextColourOnId色、FalseのときはtextColourOffIdが設定されます。
[3]では、描画のためのパラメータです。
yIndentは、テキストの上方向の余白です。proportionOfHeight(0.3f)とすることで、ボタンの高さの0.3の割合の余白をとります。最大値として、4となるようにjmin関数で最大値を設定しています。
cornerSizeは、ボタンの高さまたは、ボタンの幅の半分のどちらか小さい方のながさを格納している変数です。leftIndentまたはrightIndentの計算で使用します。
leftIndentとrightIndentはテキストの左右の余白です。textWidthを算出するために使用されます。
textWidth –(必要)–> leftIndentとrightIndent –(必要)–> cornerSize のような関係性です。
offset変数は、ボタンが押されているかいないかで2または0の値が入ります。
textWidthが0よりおおき時(leftIndentとrightIndentの合計がボタンの幅を上回っていない場合)[4]でdrawFittedText関数でテキストをボタン上に描画します。
ここで一番重要な点が、offsetを描画原点である「leftIndent + offset」,「yIndent + offset」としているところです。これで、ボタンを押した時は、右下方向に2ずれたテキストが描画されることになります。これで、ボタンが押された時にテキストが背景の矩形と同様に移動することで、ボタンが押されているような表現に見えることになります。