FireMonkey コンポーネントの設計
目次
このトピックでは、FireMonkey を使ってコンポーネントを設計する方法を説明します。
一部の組み込みコントロールの概要
FireMonkey コントロールのいくつかを調べると、FireMonkey でどのような設計が行われているかを知ることができます。
TPanel:プリミティブを使ったスタイル設定
TPanel のソース コードは短いので、全体をここに再現します。インターフェイスは次のようになっています。
TPanel = class(TStyledControl)
public
constructor Create(AOwner: TComponent); override;
published
property StyleLookup;
end;
そして、実装は次のとおりです。
constructor TPanel.Create(AOwner: TComponent);
begin
inherited;
Width := 120;
Height := 100;
end;
TPanel は、ユーザー レベルのすべてのコントロールの基底クラスである TStyledControl のサブクラスです。TPanel は StyleLookup プロパティを公開していて、そこにスタイルを検索するときの名前が含まれています。コンストラクタでは、TControl のプロパティである Width と Height を設定します。しかし、このコントロールの描画はどのように行われるのでしょうか?
FireMonkey の主要概念の 1 つは、スタイルを使用することです。Windows における TPanel のデフォルト スタイルは、.style ファイルに保存すると確認することができます。おおよそ次のようになっています。
object TRectangle
StyleName = 'panelstyle'
Width = 50
Height = 50
HitTest = False
Fill.Color = $FFffFFff
Stroke.Color = $FF8e8e8e
end
TPanel は、灰色の枠線を持つ、白い単色の長方形です (Mac ではグラデーションで塗りつぶされます)。
スタイル付きコントロールである TStyledControl には、ResourceLink プロパティがあり、TFmxObject を指しています。ResourceLink は、名前(コントロールの StyleLookup プロパティで指定した名前で、リソースの StyleName プロパティと照合されます)で検索したか、またはデフォルトの、コントロールのスタイルリソースの複製です。また、このリソースリンク オブジェクトは、コントロールが最初に読み込まれたときやコントロールの StyleLookup プロパティが変更されたときに、コントロールの最初の子として挿入されます。これは TStyledControl.ApplyStyleLookup で行われます。
そのため、コントロール(TControl)が自分自身を描画するときに、子が描画されます。最初の子はコントロール自身の外観を表すもので、その後で他の "本当の" 子が(あれば)描画されます。
プリミティブ コントロールである TRectangle は、その Paint メソッドの中でキャンバスに対して FillRect と DrawRect を呼び出して自分自身を描画します。
TCalloutPanel:スタイル契約
TCalloutPanel は、TPanel を拡張して、ビジュアルな吹き出し要素のプロパティを追加したものです。TCalloutPanel のデフォルト スタイルは、TPanel のものと実質的に同じで、おおよそ次のとおりです。
object TCalloutRectangle
StyleName = 'calloutpanelstyle'
Width = 50
Height = 50
HitTest = False
Fill.Color = $FFffFFff
Stroke.Color = $FF8e8e8e
end
最上位のスタイルは、TRectangle ではなく TCalloutRectangle(1 辺に三角形の突起が付いた長方形)です。このプリミティブは TStyledControl とまったく同じプロパティを吹き出し用に余分に持っていて、それを使って三角形を描画します。そのマッピングは ApplyStyle メソッドで次のように行われます。
procedure TCalloutPanel.ApplyStyle;
var
Back: TFmxObject;
begin
inherited;
Back := FindStyleResource('Background');
if (Back = nil) and (ResourceLink is TCalloutRectangle) then
Back := ResourceLink;
if (Back <> nil) and (Back is TCalloutRectangle) then
begin
TCalloutRectangle(Back).CalloutWidth := FCalloutWidth;
TCalloutRectangle(Back).CalloutLength := FCalloutLength;
TCalloutRectangle(Back).CalloutPosition := FCalloutPosition;
TCalloutRectangle(Back).CalloutOffset := FCalloutOffset;
end;
end;
ApplyStyle は、リソースリンク オブジェクトを設定した後に、追加プロパティの設定アクセサ メソッドごとに、ApplyStyleLookup によって呼び出されます。TCalloutPanel の場合、ApplyStyle は次のいずれかを想定しています。
- ルート オブジェクトが TCalloutRectangle である。これはデフォルト スタイルの場合に真になります。
- スタイルのコンポーネント ツリーのどこかに、StyleName が "Background" である TCalloutRectangle オブジェクトが存在する。
適切なオブジェクトが見つからなければ、TCalloutPanel は正しく機能しません。宣言された追加プロパティは効果がなくなります。
これは、コントロールのスタイルを具体化するために使われるコンポーネントは特定の想定を満たさなければならないという、スタイル契約の例です。このような特有のスタイル契約を必要とするコンポーネントを開発する場合には、コンポーネントのユーザー向けにその契約を文書化し、コンポーネント用に適切なスタイルを作成してもらえるようにすることが重要です。
TCalendar:複雑な構造
TCalendar にはデフォルトのスタイルがありません。その代わりに、コンストラクタで構築が行われます(TPanel の場合と異なり、このコンストラクタは長すぎてここに示すことができません)。実際には、TCalendar では次のコンポーネント ツリーが作成されます。
- 最上部の TLayout
- 上揃えの TGridLayout
- 曜日(日、月など)を示す 7 つの TLabel コントロール
- 最も左の TGridLayout(最初は非表示。1 列分のサイズ)
- 週番号を示す 6 つの TLabel コントロール(WeekNumbers プロパティから表示されます)
- 'transparentlistboxstyle' スタイルと AlternatingRowBackground を使用した、上揃えの 7 列の TListBox
- 42 個の TListBoxItem コントロール(6 週間を示す 6 つの行に並べられます)
コンポーネント ツリー内のどのオブジェクトも、Stored プロパティが False に、Locked プロパティが True に設定されています。Stored を無効にすると、フォーム デザイナはオブジェクトを .fmx
ファイルにストリーミング出力しません。Stored プロパティを無効にしなければ、読み込み時に冗長なサブコンポーネントが作成されます。Locked を有効にすると、ヒット テストの動作やトリガの発生方法が変化し、サブコンポーネントがより大きい全体の一部となります。
コンポーネント ツリーを作成した後、コンストラクタは protected メソッド FillList を呼び出して今月の内容を設定します。FillList では、コンポーネント ツリーにまさに含まれている直接の知識を利用して、曜日(日曜日と月曜日のどちらから始めるかはロケールによって決定します)、週番号、および日を設定します。
TCalendar では、すべてのコンポーネントの相対的な位置はハードコードされていますが、外観はほぼ完全にスタイルで制御できます。例外は、左上の 3 つのボタンに含まれる 2 つの三角形と 1 つの円です。ボタン自体はスタイル付きです。右側の月と年のポップアップは、デフォルト スタイルがラベルのデフォルト スタイルで上書きされ、(ボタンではなく)ラベルのように表示されます。曜日と週番号は TLabel のインスタンスであり、ラベルのスタイルが自動的に使われます。日を表示するための複数列リスト ボックスはスタイル付きです。最後に、個々の日のコントロールには、リスト ボックス項目のデフォルト スタイルが採用されます。
デフォルトのスタイルは持ちませんが、TCalendar の Resource プロパティが設定されるとどうなるでしょうか? そのスタイルリソースは通常どおりコントロールの最初の子として設定され、背景として描画されます。ですから、TCalendar 全体にある程度スタイルを設定できることになります。
コンポーネント ガイドライン
要約すると、FireMonkey コンポーネントの作成時には以下の点に注意してください。
- FireMonkey スタイルを使用して、アプリケーション開発者が外観を変更できるようにします。
- プリミティブ コントロールによる直接の描画部分をカプセル化し、取り替えたりサブクラス化できるようにします。
- スタイル契約は文書化する必要があります。
- 必要なコンポーネントの検索は StyleName を使用するのが最も簡単なので、適切な StyleName を設定します。
- 複雑なコントロールは、スタイル付きコンポーネントを使ってコードで構築することができます。