本記事は JUCE Advent Calendar 2021 の12月15日向けに投稿した記事です。
マルチメディア向けC++フレームワークであるJUCEには、独自のGUIライブラリが含まれています。本記事では、JUCE独自のGUIライブラリを使いこなすにあたって知っておくといつか役に立つであろう、JUCEの中身について紹介します。
JUCEのGUI要素について
多くのGUIフレームワークでは、ウインドウ内にGUI要素(コンポーネント、ウィジェット、アイコン等)を配置していくことでGUIを構築する仕組みを採用しています。JUCEフレームワークにおいても考え方は同様です。JUCEにおいてGUI要素の基本実装はjuce::Component
クラス*1によって提供されます。
開発者は、juce::Component
を継承したクラスを宣言・定義をすることで、独自のGUI要素(以下コンポーネントと呼ぶ)の実装をアプリケーションコードに追加してウインドウ内に配置することができます。
juce::Component
のメンバ変数properties
について
juce::Component
クラスのソースコードを見てみると、juce::NamedValueSet
クラス型の変数であるproperties
というメンバ変数を確認することができます。*1
このjuce::NamedValueSet
クラス型とは、juce::NamedValueSet::NamedValue
クラス型を扱うコンテナであり、開発者はこのクラス型の変数をKey-Valueストアとして利用することができます。*2
またjuce::NamedValueSet::NamedValue
クラスは、juce::Identifier
クラス型で表される「文字列による識別子」と、juce::var
クラス型で表されるバリアント型で表される「様々な型を格納可能なデータ」のペアを表現するクラスとして定義されています。*3
*1 juce::Componentクラスのヘッダ
*2 juce::NamedValueSetクラス
*3 juce::NamedValueSet::NamedValueクラス
properties
へのアクセス方法について
juce::Component::getProperties
関数*1を介してjuce::Component
インスタンスが保持するproperties
にアクセスすることができます。非const関数が提供されているので、非const関数を介してproperties
の値を変更することができます。
//==============================================================================
/** Returns the set of properties that belong to this component.
Each component has a NamedValueSet object which you can use to attach arbitrary
items of data to it.
*/
NamedValueSet& getProperties() noexcept { return properties; }
/** Returns the set of properties that belong to this component.
Each component has a NamedValueSet object which you can use to attach arbitrary
items of data to it.
*/
const NamedValueSet& getProperties() const noexcept { return properties; }
*1 juce::Component::getProperties関数
properties
があると何ができる?
既に述べた通り、juce::Component
のメンバ変数properties
は「文字列による識別子」と「様々な型を格納可能なデータ」のペアを複数格納することができるKey-Valueストアです。
このメンバ変数を利用することで、juce::Component
およびそれを継承したクラスは、型に縛られない任意のデータを各インスタンス毎に個別に保持することができるようになります。
properties
の利用用途: JUCE標準のColour系メソッド
juce::Component
クラスのColour系メソッド
juce::Component
クラスにはコンポーネント描画時のColour設定をAPIを介して行える仕組みが予め用意されています。
これらのメソッドの内部実装は、int型で受け取った値をColourIdの文字列に変換したものをKeyとし、juce::Colour型の値をint型にキャストしたものをValueとするKey-Valueペアのデータをproperties
変数にGet/Setするものとなっています。
■ juce_Component.h
//==============================================================================
/** Looks for a colour that has been registered with the given colour ID number.
If a colour has been set for this ID number using setColour(), then it is
returned. If none has been set, the method will try calling the component's
LookAndFeel class's findColour() method. If none has been registered with the
look-and-feel either, it will just return black.
The colour IDs for various purposes are stored as enums in the components that
they are relevant to - for an example, see Slider::ColourIds,
Label::ColourIds, TextEditor::ColourIds, TreeView::ColourIds, etc.
@see setColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour
*/
Colour findColour (int colourID, bool inheritFromParent = false) const;
/** Registers a colour to be used for a particular purpose.
Changing a colour will cause a synchronous callback to the colourChanged()
method, which your component can override if it needs to do something when
colours are altered.
For more details about colour IDs, see the comments for findColour().
@see findColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour
*/
void setColour (int colourID, Colour newColour);
/** If a colour has been set with setColour(), this will remove it.
This allows you to make a colour revert to its default state.
*/
void removeColour (int colourID);
/** Returns true if the specified colour ID has been explicitly set for this
component using the setColour() method.
*/
bool isColourSpecified (int colourID) const;
■ juce_Component.cpp
Colour Component::findColour (int colourID, bool inheritFromParent) const
{
if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyID (colourID)))
return Colour ((uint32) static_cast<int> (*v));
if (inheritFromParent && parentComponent != nullptr
&& (lookAndFeel == nullptr || ! lookAndFeel->isColourSpecified (colourID)))
return parentComponent->findColour (colourID, true);
return getLookAndFeel().findColour (colourID);
}
bool Component::isColourSpecified (int colourID) const
{
return properties.contains (ComponentHelpers::getColourPropertyID (colourID));
}
void Component::removeColour (int colourID)
{
if (properties.remove (ComponentHelpers::getColourPropertyID (colourID)))
colourChanged();
}
void Component::setColour (int colourID, Colour colour)
{
if (properties.set (ComponentHelpers::getColourPropertyID (colourID), (int) colour.getARGB()))
colourChanged();
}
JUCE標準ウィジェットのColourIdsについて
これらAPIの使い方は、juce_gui_basics
モジュールに含まれるスライダー等のウィジェットを見てみることで、具体的な利用方法を確認することができます。例えば、juce::Slider
クラス*1を実例としてリファレンス及びソースコードを見てみると、juce::Slider
クラスにはenum型のjuce::Slider::ColourIds
の定義が追加されていることが確認できます。
■ juce_Slider.h
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the slider.
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
methods.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
backgroundColourId = 0x1001200, /**< A colour to use to fill the slider's background. */
thumbColourId = 0x1001300, /**< The colour to draw the thumb with. It's up to the look
and feel class how this is used. */
trackColourId = 0x1001310, /**< The colour to draw the groove that the thumb moves along. */
rotarySliderFillColourId = 0x1001311, /**< For rotary sliders, this colour fills the outer curve. */
rotarySliderOutlineColourId = 0x1001312, /**< For rotary sliders, this colour is used to draw the outer curve's outline. */
textBoxTextColourId = 0x1001400, /**< The colour for the text in the text-editor box used for editing the value. */
textBoxBackgroundColourId = 0x1001500, /**< The background colour for the text-editor box. */
textBoxHighlightColourId = 0x1001600, /**< The text highlight colour for the text-editor box. */
textBoxOutlineColourId = 0x1001700 /**< The colour to use for a border around the text-editor box. */
};
ColourIdsを使用してColour系メソッドを呼ぶ
前述したjuce::Component
クラスのColour系メソッドを使うことで、ColourIdsの値をKeyとしてjuce::Colour
型の値をValueとするKey-Valueペアをコンポーネントのproperties
に追加します。
この辺りの詳細は公式サイトのチュートリアルでも触れられています。*2
■ juce::Slider
のオブジェクトに対してAPIからColour設定を行う例
juce::Slider slider;
slider.setColour (juce::Slider::ColourIds::thumbColourId, juce::Colours::red);
*1 juce::Slider::ColourIds
*2 Tutorial: Colours in JUCE
コンポーネントの描画メソッド内でColour設定を参照する
コンポーネントを描画するメソッドにおいて、properties
メンバ変数に保持されている「ColourIds-juce::Colour
」のペアを取得することで、開発者がAPIを介して設定したColour設定を描画メソッドに反映することができます。
juce::Slider
クラスのコンポーネント描画メソッドの場合を例に示すと、juce::Slider
クラスの描画メソッドは juce::LookAndFeel
の派生クラスに置かれています。以下にjuce::LookAndFeel_V4::drawRotarySlider
のコードの一部を抜粋します。*1
■ juce_LookAndFeel_V4.cpp
void LookAndFeel_V4::drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider)
{
...
g.setColour (slider.findColour (Slider::thumbColourId));
g.fillEllipse (Rectangle<float> (thumbWidth, thumbWidth).withCentre (thumbPoint));
}
まとめ
本記事では、juce::Component
のメンバ変数properties
について、その仕組みや用途について語ってみました。
また、properties
が提供するKey-Valueストア機能の利用用途の実例として、JUCE標準で用意されているColour系メソッドの内部実装についても紹介しました。
properties
という仕組みについて把握しておくと、カスタムコンポーネントを実装する際に役に立つと思われます。