メインコンテンツまでスキップ

チュートリアル:Slider クラス

📚 Source Page

このチュートリアルでは、Sliderクラスを紹介し、スライダーの動きに応答する方法と、スライダーから値を取得する方法を示します。また、スライダーで値を表示するための基本的なカスタマイズ技術も紹介します。

レベル: 初級
プラットフォーム: Windows, macOS, Linux, iOS, Android
クラス: Slider, Slider::Listener, Label

はじめに

ヒント

このチュートリアルはチュートリアル:リスナーとブロードキャスターから続いており、最初にそれを読んで理解しておく必要があります。

このチュートリアルのデモプロジェクトをこちらからダウンロードしてください:PIP | ZIP。プロジェクトを解凍し、最初のヘッダファイルを Projucer で開いてください。

この手順でサポートが必要な場合は、チュートリアル:Projucer Part 1: Projucer を始めようを参照してください。

デモプロジェクト

デモプロジェクトは 2 つの水平線形スライダーを表示します。1 つのスライダーにはFrequencyというラベルが付けられ、もう 1 つにはDurationというラベルが付けられています。以下のスクリーンショットに示されています:

2つのスライダーとその値を示すデモプロジェクトのユーザーインターフェース
2つのスライダーとその値を示すデモプロジェクトのユーザーインターフェース

両方のスライダーは、周波数(f)が持続時間(d)の逆数であるため、本質的に同じ基礎値を表示するという考えです:

f = 1d

どちらかのスライダーが動かされると、もう一方が変更を反映して更新されます。

JUCE Slider クラス

このチュートリアルでは、スライダーの作成、範囲の設定、値の変更のリッスン、およびプログラム的にスライダーの値を更新する方法を示します。デモアプリケーションの実行時に、両方のスライダーにテキストボックスが含まれていることに気づくでしょう。このテキストボックスには、周波数の単位(Hz、ヘルツ)と持続時間の単位(s、秒)も含まれています。

スライダーの追加

スライダーはMainContentComponentクラスのプライベートメンバーとして追加されています:

private:
juce::Slider frequencySlider;
juce::Label frequencyLabel;
juce::Slider durationSlider;
juce::Label durationLabel;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

Sliderオブジェクトに対してLabelオブジェクトも追加していることに注意してください。これらは、スライダーの左側にFrequencyDurationというテキストを表示するためのものです。現在のスライダー値を表示するスライダーコントロールのすぐ左側のボックスは、実際にはSliderオブジェクトの一部です。

また、スライダーの変更を受信するためにクラスを登録できるよう、Slider::Listenerクラスを基底クラスとして追加しています:

class MainContentComponent : public juce::Component,
public juce::Slider::Listener
{
public:

MainContentComponentコンストラクタでは、スライダーを子コンポーネントとして追加し(チュートリアル:親コンポーネントと子コンポーネントを参照)、可視化して、スライダーが表す値の範囲を設定します。まず、frequencySliderメンバーを設定します:

MainContentComponent()
{
addAndMakeVisible (frequencySlider);
frequencySlider.setRange (50, 5000.0); // [1]
frequencySlider.setTextValueSuffix (" Hz"); // [2]
frequencySlider.addListener (this); // [3]
  • [1]:スライダーの範囲はSlider::setRange()関数を使用して設定します。
  • [2]:スライダーのテキストボックスのテキスト表示に接尾辞を追加して、値の単位を表示します。
  • [3]:MainContentComponentオブジェクトをスライダーのリスナーとして追加します。

対応するラベルは以下のように設定されます:

addAndMakeVisible (frequencyLabel);
frequencyLabel.setText ("Frequency", juce::dontSendNotification);
frequencyLabel.attachToComponent (&frequencySlider, true); // [4]

Label::attachToComponent()関数[4]は、別のコンポーネントの隣にラベルを配置するのに非常に便利です。2 番目の引数trueは、ラベルを他のコンポーネントの左側に配置します(falseは上に配置します)。後で見るように、これによりMainContentComponent::resized()関数でラベルを手動で配置する必要がなくなります。

durationSliderdurationLabelメンバーも同様に設定されますが、このスライダーの範囲はfrequencySliderメンバーの範囲の逆数に設定されます:

addAndMakeVisible (durationSlider);
durationSlider.setRange (1.0 / frequencySlider.getMaximum(),
1.0 / frequencySlider.getMinimum());
durationSlider.setTextValueSuffix (" s");
durationSlider.addListener (this);

addAndMakeVisible (durationLabel);
durationLabel.setText ("Duration", juce::dontSendNotification);
durationLabel.attachToComponent (&durationSlider, true);

スライダーの配置

スライダーはMainContentComponent::resized()関数で配置されます。Label::attachToComponent()関数を使用してラベルをスライダーにアタッチしたため、これらはスライダーの左側に自動的に配置されます。

void resized() override
{
auto sliderLeft = 120;
frequencySlider.setBounds (sliderLeft, 20, getWidth() - sliderLeft - 10, 20);
durationSlider.setBounds (sliderLeft, 50, getWidth() - sliderLeft - 10, 20);
}

スライダーの変更への応答

以下のコードは、スライダーのリスナーがスライダーの値の変更に反応するようにします。

void sliderValueChanged (juce::Slider* slider) override
{
if (slider == &frequencySlider)
durationSlider.setValue (1.0 / frequencySlider.getValue(), juce::dontSendNotification);
else if (slider == &durationSlider)
frequencySlider.setValue (1.0 / durationSlider.getValue(), juce::dontSendNotification);
}

これは、Slider::Listenerを基底クラスとして追加した場合にオーバーライドする必要があるSlider::Listener::sliderValueChanged()関数です。ここでは、Slider::setValue()関数を呼び出すことで、スライダーの逆数を他のスライダーに渡すだけです。また、スライダーにその変更をブロードキャストしないように伝えます。これは、2 つのスライダーが互いに依存しているこのような場合に、無限のフィードバックループが発生する可能性があるためです。dontSendNotificationの値はこの潜在的なループを中断します。算術が正確で、両方向の変換が同一の結果を生成すると仮定すると、これは必要ないはずです。これは、値が実際に変更された場合にのみスライダーがリスナーにブロードキャストするためです。(このような状況で変換にわずかな丸め誤差がある場合に問題が発生する可能性があります。)dontSendNotificationの値を省略してみることができます。これにより、スライダーが変更をブロードキャストするデフォルトの動作が発生します。独自のアプリケーションの特定のユースケースでdontSendNotificationを使用するかどうかを慎重に検討する必要があります。

初期値の設定

コンストラクタでfrequencySliderスライダーは 500 の値に設定されます。これにより、今回はdontSendNotificationの値を省略するため、durationSliderスライダーが更新されます:

frequencySlider.setValue (500.0); // [5]

いくつかのカスタマイズ

インターフェースをより効果的にするために、いくつかのシンプルなカスタマイズを追加できます。

テキストボックスを広くする

特にdurationSliderスライダーのテキストボックスは、値を満足に表示するために多くの桁が必要です。これを行うには、Slider::setTextBoxStyle()関数を使用できます。MainContentComponentコンストラクタに以下の 2 行のコードを追加します:

frequencySlider.setTextBoxStyle (juce::Slider::TextBoxLeft, false, 160, frequencySlider.getTextBoxHeight());
durationSlider.setTextBoxStyle (juce::Slider::TextBoxLeft, false, 160, durationSlider.getTextBoxHeight());

これにより、各ケースでテキストボックスが 160 ピクセルに設定されます(ただし、Slider::getTextBoxHeight()関数を使用して高さを維持します)。

より広いテキストボックスのスライダー
より広いテキストボックスのスライダー

スライダー値のスキュー

デフォルトでは、スライダートラックは線形です。つまり、スライダーの値はスライダートラックに沿ったスライダーサムの位置に比例します。インターフェースを操作すると、これが正しくないと感じることがわかります。スライダースキューを調整して、スライダートラックを対数にすることができます。これを行うには、Slider::setSkewFactorFromMidPoint()関数を使用できます。スライダーが設定された後、MainContentComponentコンストラクタに以下の 2 行のコードを追加して試してみてください:

frequencySlider.setSkewFactorFromMidPoint (500);
durationSlider.setSkewFactorFromMidPoint (0.002);

これにより、frequencySliderスライダーのスライダートラックの中央に 500 の値が配置され、durationSliderスライダーには 0.002 が配置されます。効果的に、スライダーは同じように動くように見えますが、反対方向に動きます。このような非線形のスライダートラックは、時間や周波数などのパラメータでうまく機能します。小さい値に対してより細かい制御が必要ですが、大きい値に対してはそれほど細かい制御を必要としない場合です。

ヒント

このセクションの完成したコードは、このチュートリアルのデモプロジェクトのSliderValuesTutorial_02.hファイルにあります。

注記

演習:Slider::setSkewFactorFromMidPoint()関数の呼び出しに異なる値を試したり、異なるテキストボックスサイズを試したりしてください。Sliderクラスの API リファレンスを見て、他のカスタマイズを試してください。

Slider コールバックの簡素化

このチュートリアルで示されているリスナーとブロードキャスターのパラダイムを使用する代わりに、最新の C++標準のラムダ関数を使用してスライダーコールバックを簡素化できます。これは、複雑な実装を必要としないシンプルなコールバックに特にうまく機能します。

まず、Slider::Listenerクラスからの継承を削除し、MainContentComponent クラスの定義を以下のように復元します:

class MainContentComponent : public juce::Component
{
public:

次に、MainContentComponent をSliderのリスナーとして追加する代わりに、Slider::onValueChangeヘルパーオブジェクトにラムダ関数を割り当てます:

MainContentComponent()
{
addAndMakeVisible (frequencySlider);
frequencySlider.setRange (50, 5000.0);
frequencySlider.setTextValueSuffix (" Hz");
frequencySlider.onValueChange = [this] { durationSlider.setValue (1.0 / frequencySlider.getValue(), juce::dontSendNotification); };

addAndMakeVisible (frequencyLabel);
frequencyLabel.setText ("Frequency", juce::dontSendNotification);
frequencyLabel.attachToComponent (&frequencySlider, true);

addAndMakeVisible (durationSlider);
durationSlider.setRange (1.0 / frequencySlider.getMaximum(),
1.0 / frequencySlider.getMinimum());
durationSlider.setTextValueSuffix (" s");
durationSlider.onValueChange = [this] { frequencySlider.setValue (1.0 / durationSlider.getValue(), juce::dontSendNotification); };

これは、ユーザーがSliderの値を変更したときに呼び出す関数をSliderオブジェクトに伝えます。

ヒント

このコードの実装は、このチュートリアルのデモプロジェクトのSliderValuesTutorial_03.hファイルにあります。

まとめ

このチュートリアルでは、Sliderクラスを紹介しました。特に、以下のことを学びました:

  • 特定の範囲で動作するようにスライダーを設定する方法。
  • スライダー値の変更に応答する方法。
  • 対数スケールを使用するようにスライダースキューを設定する方法。

関連項目