ラジオボタンとか、結構使いそうなので、作り方を覚えておきたいですね。
トグルボタンを組み合わせて作る点がポイントだね~
JUCE、「Interface Design」の「Radio buttons and checkboxes」チュートリアルです。ラジオボタンとチェックボックスをトグルボタンコンポーネントで実装していく、基本的な実装になります。チュートリアルのデモアプリを実装しながら、扱い方を学習していきます。
公式のチュートリアルページはこちらになります。
https://docs.juce.com/master/tutorial_radio_buttons_checkboxes.html
こんな人の役に立つかも
・JUCEプログラミングを勉強している人
・JUCEチュートリアル「Radio buttons and checkboxes」をやっている人
・JUCEでラジオボタンとチェックボックスを扱いたい人
デモアプリの概要
「Male」または「Female」を排他的に選択するラジオボタン形式のコンポーネントと、「Sport」「Art」「Film」を同時に複数選択可能なチェックボックスのコンポーネントをアプリとして実装していきます。こんな感じのみためになります。
デモアプリ実装
プロジェクトの準備
Projucerの「GUI」テンプレートから作成します。
MainComponent.h
MainComponentクラスに以下のメンバを追加しました。
class MainComponent : public juce::Component
{
public:
//...略...
//[1]enumを追加します。
enum RadioButtonIds
{
GenderButtons = 1001
};
//[2]updateToggleState関数を追加します。
void MainComponent::updateToggleState(juce::Button* button, juce::String name);
private:
//[3]以下のボタンとラベルを追加します。
juce::Label genderLabel{ {}, "I identify my gender as..." };
juce::ToggleButton maleButton{ "Male" },
femaleButton{ "Female" };
juce::Label hobbiesLabel{ {}, "My hobbies are..." };
juce::ToggleButton sportButton{ "Sport" },
artButton{ "Art" },
filmButton{ "Film" };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
[1]は、ラジオボタングループとしてトグルボタンをまとめるIDをこのように定義しています。後ほど、トグルボタンコンポーネントにsetRadioGroupIdでこのIDを指定することで、同じID同士のトグルボタンがラジオボタンの一員として機能するようになります。ラジオボタンのIDを言葉としてまとめて列挙しているenumになります。
[2]で、ラジオボタン、チェックボックスそれぞれのリスナー関数の処理内容として実行する関数をupdateToggleState関数として定義します。
[3]で、Labelコンポーネントを2つとToggleButtonコンポーネントを5つ定義します。ToggleButtonコンポーネントについては、初期値に「Male」と「Female」と与えたものはラジオボタンの構成要素、「Sport」「Art」「Film」と与えたものはチェックボックスの構成要素としてのトグルボタンとして設定する予定です。
トグルボタンコンポーネントからラジオボタンとチェックボックスができるんですね。
MainComponent.cpp
コンストラクタ
コンストラクタを以下の内容に変更します。
MainComponent::MainComponent()
{
addAndMakeVisible(genderLabel);
//[1]ラジオボタンを構成します。
//[1-1]トグルボタンの可視化です。
addAndMakeVisible(maleButton);
addAndMakeVisible(femaleButton);
//[1-2]選択時に実行される処理を紐付けます。
maleButton.onClick = [this] { updateToggleState(&maleButton, "Male"); };
femaleButton.onClick = [this] { updateToggleState(&femaleButton, "Female"); };
//[1-3]ラジオボタンとして構成します。
maleButton.setRadioGroupId(GenderButtons);
femaleButton.setRadioGroupId(GenderButtons);
addAndMakeVisible(hobbiesLabel);
//[2]チェックボックスを構成します。
addAndMakeVisible(sportButton);
addAndMakeVisible(artButton);
addAndMakeVisible(filmButton);
sportButton.onClick = [this] { updateToggleState(&sportButton, "Sport"); };
artButton.onClick = [this] { updateToggleState(&artButton, "Art"); };
filmButton.onClick = [this] { updateToggleState(&filmButton, "Film"); };
setSize(400, 300);
}
[1]では、ラジオボタンを構成します。ラジオボタンは、構成要素の一つのみが排他的(一つがオンなら他はオフ)になるようなものです。
[1-1]はaddAndVisible関数でmaleとfemaleのトグルボタンを可視化します。[1-2]では、onClick関数にupdateToggleState関数を実行するようなラムダ式を記載しています。updateToggleState関数の処理内容は、後ほど記載します。
[1-3]がラジオボタンを構成する上でもっとも重要で、enumで定義したID「GenderButtons」をButtonクラスのsetRadioGroupId関数で指定しています。同じID同士のトグルボタンが排他的になり、ラジオボタンを構成するようになります。ここでポイントなのが、setRadioGroupId関数はButtonクラスの関数です。そのため、ToggleButtonクラスのコンポーネントでなくてもButtonクラスを継承している各種ボタンでも、ラジオボタンを構成することができます。
[2]はチェックボックスを構成しています。チェックボックスは、単純にトグルボタンコンポーネントを並べているというだけになります。
paint関数
paint関数では、背景の塗りつぶしのみとします。
void MainComponent::paint (juce::Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
}
resized関数
全てのコンポーネントをsetBounds関数でGUIに並べています。setBounds関数の最初の2つの引数がコンポーネントの左上原点の位置になり、後ろ2つの引数がコンポーネントの幅、高さを指定しています。今回のデモアプリは、ハードコーディングでの位置指定になります。
void MainComponent::resized()
{
genderLabel.setBounds(10, 10, getWidth() - 20, 20);
maleButton.setBounds(20, 40, getWidth() - 30, 20);
femaleButton.setBounds(20, 70, getWidth() - 30, 20);
hobbiesLabel.setBounds(10, 110, getWidth() - 20, 20);
sportButton.setBounds(20, 140, getWidth() - 30, 20);
artButton.setBounds(20, 170, getWidth() - 30, 20);
filmButton.setBounds(20, 200, getWidth() - 30, 20);
}
updateToggleState関数
最後に、今回の全てのトグルボタンに紐付けた関数で、全てのトグルボタンが選択された時に実行される関数、updateToggleState関数をcppの一番下に定義しました。
//[1]引数はbuttonコンポーネントのポインタと、文字列です。
void MainComponent::updateToggleState(juce::Button* button, juce::String name)
{
//[2]トグルボタンの状態を取得します。
auto state = button->getToggleState();
//[3]3項演算子でONまたはOFFの文字列を取得します。
juce::String stateString = state ? "ON" : "OFF";
//[4]デバッガに文字列を表示します。
juce::Logger::outputDebugString(name + " Button changed to " + stateString);
}
updateToggleState関数は、[1]のように、第一引数にButtonクラスのオブジェクトのポインタをとります。コンストラクタのonClick関数のラムダ式での引数の与え方を見ると、それぞれのToggleButtonコンポーネントのアドレス(&maleButtonのように)を与えています。そのため、この関数内でbuttonという名称はこの関数を呼び出したButtonオブジェクト(コンポーネント)を示すことになります。buttonは、Buttonクラスオブジェクトのポインタなので、それぞれの関数を呼び出すためにはアロー演算子を利用します。
第二引数では、juceのStringクラスとして文字列を受け取ります。
[2]のように、アロー演算子でbuttonのgetToggleState関数を呼び出します。これで、トグルボタンの状態をtrueまたはfalseで取得します。このbool値はstate変数に格納します。
[3]では、 先ほど取得したstateの値によって「on」または「off」の文字列をstateStringに格納します。3項演算子を利用して、trueの場合「on」という文字列、falseの場合「off」という文字列をstateStringに格納します。
[4]では、juce::LoggerクラスのoutputDebugString関数で、デバッガに文字列として「name」として引数で受け取った文字列と「Button changed to」という文字列、stateStringの文字列を結合したものを表示させます。