プログラムを実行して表が目で見て確認できるようにしましょう。
チュートリアルではまとめて実装後に表示なので機能毎にどのように変化したかがわかりにくいね〜
JUCEチュートリアル、「Interface Design」、「The TableListBox class」チュートリアルです。前回までで、XMLからデータを読み込んで表のコンポーネントを作成するところまでやりました。チュートリアルでは、表の項目にテキスト入力コンポーネントなどを埋め込むようなチュートリアルとなりますが、このチュートリアルは、最後までやっていかないと実行結果が確認できないてんが少しわかりづらいと感じたので、この時点までで表として出力されているか確認できるように、表の描画処理を追記していこうと思います。チュートリアルでは、「Setting the Model」項目の前半部分に該当します。
公式のチュートリアルページはこちらになります。
https://docs.juce.com/master/tutorial_table_list_box.html
表を可視化する
MainComponent.hに実装しているTableTutorialComponentクラスを改造していきます。以前の記事の「必要最低限のTableTutorialComponent」をベースに改造していきます。
https://panda-clip.com/juce-the-tablelistbox-class2
コンストラクタ
以下の1~4のプログラムを追加しました。
class TableTutorialComponent : public juce::Component,
public juce::TableListBoxModel
{
public:
TableTutorialComponent()
{
loadData();
//[1]追加します。
addAndMakeVisible(table);
//[2]表の枠線を描画する処理です。追加しました。
table.setColour(juce::ListBox::outlineColourId, juce::Colours::grey);
table.setOutlineThickness(1);
if (columnList != nullptr)
{
//...略...
}
//[3]「ID」列基準でデータをソートします。
table.getHeader().setSortColumnId(1, true);
//[4]表の行を複数選択できるようにします。
table.setMultipleSelectionEnabled(true);
}
[1]で、tableを可視化します。
[2]では、表の周りに2ピクセルの線幅の枠を描画しています。線の色はsetColorでgreyにしています。カラーIDはこちらのドキュメントに定義されています。みづらいですが、次のスクショの上が下のように枠線付となりました。
[3]のgetHeader関数は、TableListBoxクラスの関数で、表で利用されているTableHeaderComponentクラスのオブジェクトを取得できます。そして、setSortColumnId関数で「ID」順にデータを並べるように指示しています。
[4]では、ListBoxクラスのsetMultipleSelectionEnabled関数で行の複数選択ができるようにしています。このプログラムを追加すると、次のスクショのように、行の複数選択ができます。
たくさん継承しているので、どこのクラスの関数なのか、わかりにくいですね~
paintRowBackground関数
paintRowBackground関数をオーバーライドして、行の背景の色を塗りつぶす処理をカスタマイズしていきます。
void paintRowBackground(juce::Graphics& g, int rowNumber, int /*width*/, int /*height*/, bool rowIsSelected) override
{
//[1]少し薄い背景色を取得します。
auto alternateColour = getLookAndFeel().findColour(juce::ListBox::backgroundColourId)
.interpolatedWith(getLookAndFeel().findColour(juce::ListBox::textColourId), 0.03f);
//[2]行が選択されている時と選択されていない時の行の背景色を設定します。
if (rowIsSelected)
g.fillAll(juce::Colours::lightblue);
else if (rowNumber % 2)
g.fillAll(alternateColour);
}
[1]では、デフォルトの背景色(濃いグレー)とListBoxクラスの文字の色(白)との変化量を1とした時の0.03の値の色、グレーが少しだけ白よりになった色をalternateColourに取得しています。「getLookAndFeel().findColour(juce::ListBox::backgroundColourId)」までで、colourオブジェクトが返され、このcolourオブジェクトからinterpolatedWith関数を呼び出しています。
[2]では、引数として受け取る「rowIsSelected」には行が選択されたかどうかのboolが入ってくるので、これをみて、選択された行の色を変更する、という処理を記載していきます。選択状態の場合、trueなので、背景をfillAll関数でlightblueにします。選択されていない時(else ifの条件)は、rowNumber%2という剰余演算で偶数の場合0となり、奇数行では1となります。奇数行の場合、薄いグレーのalternateColoueで塗りつぶされます。そのため、表は行毎に交互に色が変化するようになっています。
paintCell関数
paintCell関数は、表のセル内の描画をする関数です。今回は、ここでデータの文字列を描画しますので、これを実装しないと、表には何も表示されません。デバッグで確認したところ、全てのセルの描画を一つずつ行うようです。
図のように、左上から列方向に、そして、行方向にとセルを一つづつ描画します。1セル毎にpaintCellが呼び出されます。
void paintCell(juce::Graphics& g, int rowNumber, int columnId,
int width, int height, bool rowIsSelected) override
{
//[1]行が選択されている場合、色をdarkblueに、それ以外は通常の色に変更です。
g.setColour(rowIsSelected ? juce::Colours::darkblue : getLookAndFeel().findColour(juce::ListBox::textColourId));
g.setFont(font);//[2]フォントサイズの設定です。
//[3]rowNumber行目のデータを取得します。
if (auto* rowElement = dataList->getChildElement(rowNumber))
{
//[3-1]行の中のカラムIDのデータを取得します。
auto text = rowElement->getStringAttribute(getAttributeNameForColumnId(columnId));
//[3-2]テキストをセルに描画します。
g.drawText(text, 2, 0, width - 4, height, juce::Justification::centredLeft, true);
}
//[4]セルの右側に区切り線を描画します。
g.setColour(getLookAndFeel().findColour(juce::ListBox::backgroundColourId));
g.fillRect(width - 1, 0, 1, height);
}
[1]では、描画色を設定します。行が選択されているかどうかのbool値「rowIsSelected」を条件にして、選択されている場合はdarkblue、それ以外はデフォルトの色(IDから取得した色)としています。この色は後程テキスト描画のときの色となり、次のように選択された行のセルの文字の色となって反映されます。
[2]では、privateなメンバに定義したフォントクラスを使い文字サイズを設定します。
[3]で、行データが存在しているかを判定します。行のデータが存在したら、セルのデータを取得しに行きます。
[3-1]は、XmlElementクラスのgetStringAttribute関数で列名称のデータを取得します。列名を取得する必要があるので、列のID番号であるcolumnID変数から、属性名を取得する関数を後ほどgetAttributeNameForColumnId関数として定義します。この処理のポイントとして、データの構成も重要で、<DATA>の属性名を<COLUMN>と同じ数、同じ順番に並べたフォーマットなのでこのように処理できます。
[3-2]では、このtextをdrawText関数で描画しています。左に2ピクセルの余白を開けて、高さ方向に中央、横方向に左よりとしてテキストを描画します。
getAttributeNameForColumnId関数
この関数で、与えたcolumnIDの「name」属性を文字列として取得します。
juce::String getAttributeNameForColumnId(const int columnId) const
{
//[1]for文はチュートリアルのものでは動作しませんので変更しています。
for (auto* columnXml = columnList->getFirstChildElement();
columnXml != nullptr;
columnXml = columnXml->getNextElement())
{
//[2]該当する番号のname属性の文字列を取得します。
if (columnXml->getIntAttribute("columnId") == columnId)
return columnXml->getStringAttribute("name");
}
return {};
}
[1]は、チュートリアルでは動作しないので、以前紹介した記事の不具合修正の内容を反映しています。正しく動作するfor文に変更しています。for文では、columnListの要素数分繰り返しを行います。columnListには次の<HEADERS>のデータがloadData関数で取得されています。
for文でcolumnXMLに<COLUMN …>データが順番に格納されますので、columnXmlのnameを取得することで、表の列名を文字列として取得することができます。[2]のように、columnIdと引数で受け取ったcolumnId(同じ名前なので紛らわしいですが、引数として受け取った値と、XMLデータのcolumnIdという違いがあります。^^;)が同じ数値であればそのnameをgetStringAttribute関数で文字列として取得してreturnしています。