2013年12月22日日曜日

FireMonkey で閉じるボタン付きの TabItem を作る

Delphi Advent Calendar 2013 12/22 の記事です。

FireMonkey で「閉じるボタン付きの TabItem」を作ってみました。
これは、そもそもは facebook の Delphi Talks グループで田中さんが「格好いい?「PageControl 」を探しています」というスレッドを立てたので、僕が FireMonkey で作るのはいかがですか!とオススメした所から始まっています。
ということで、FireMonkey でコンポーネントをるのは大変ではないので、作りました
でも、うかうかしているうちに、やましょうさんの前日の Advent Calendar

> Fmxで作成してみるとわかるのですが、
> delphi+fmxって
> 本当に凄い!!
>
> だってこんなコンポーネントが簡単にできるんですよ。。
> しかも、携帯端末(iosなど)で動くのですから。。

と、言いたいことを言われてしまいました!!/(^o^)\
これが Delphi Advent Calendar の怖いところ!!早い者勝ちなので、来年からは書きたいことがあったら、もっと早く書こう…

まあ、それはそれとして、FireMonkey で、コンポーネントを作る方法は大きく分けて次の3段階が必要です。
  1. スタイルを作る
  2. コンポーネントを作る
  3. コンポーネントエディタを作る
VCL のコンポーネントと大きく違うのは1番目のスタイルを作る部分です。
FireMonkey の場合、見た目の制御は、ほとんど全部スタイルに任せます。
ですが!スタイルを1から作るのはメンドクサイ!
大体が TShape から派生した TRectangle とか TCircle とか TText とかを組み合わせて作るのですが、そういった簡単な図形以外は TPath を使って作ります。
TPath は SVG とか XAML のパスデータを描画してくれるコントロールです。
今回の「閉じるボタン」は「×」の形をしているので、単純な Shape を組み合わせて作るのは大変です!
なので、今回はズルします!
既に「×」ボタンを持っているコントロールからスタイルを盗みましょう。
そのコントロールとは TClearingEdit です。



ということで、方針が定まったのでスタイルを作っていきます。まずは、TStyleBook をフォームに貼ります。



そして、TStyleBook をダブルクリックしてスタイルエディタを開きます。



そして、デフォルトのスタイルから、まあ、なんでもいいんですが、ここでは構造が簡単な Dark スタイルを読み込みました。

「適用して閉じる」を押して、Style エディタを閉じます。
そして、TForm の StyleBook に StyleBook1 を設定します。



次に TTabControl を Form に貼ります。TTabControl は Common Controls にあります。
貼ったら右クリックしてコンポーネントエディタを開いて TTabItem を1つ作りましょう。
そして、できた TTabItem を右クリックして「カスタムスタイルの編集...」を押します。



すると!先ほど作った TabItem1 専用の Style が自動的に作られます。



これを編集していきます。
そして、先ほど盗んでくる!と言った TClearingEdit の「×」印を盗みます!



これを、そのまま TTabItem に移動しちゃいます。



そして、「適用して閉じる」を押して、スタイルエディタを閉じます。



フォームに戻ってみると……切れてる!!
そうです。「×」印が増えた分、文字が切れてしまったのです。

これは、ソースを修正しないと直せないので、スタイルの編集はここまでにして、次にソースを作っていきます……と、その前に作った Style を保存します。

StyleBook をダブルクリックして、スタイルエディタを開き、「保存」ボタンを押します。



すると、Style がテキストで保存されます。
なので、できあがった tabitem1style1 の "object TLayer" で表されるブロックを残して、あとは消します。



そして、これを保存しておきます。

さて、ではソースを作っていきます。
閉じるボタンが付いている TabItem を、TTabItemWithClose という名前にしました。

unit FMX.TabItemWithClose;
 
interface
 
uses
System.Classes
, FMX.Controls
, FMX.TabControl
, FMX.StdCtrls
;
 
type
[ComponentPlatformsAttribute(pidWin32 or pidWin64 or pidOSX32)]
TTabItemWithClose = class(TTabItem)
public type
TCloseEvent = procedure (Sender: TObject; var ioDoClose: Boolean) of object;
private const
STYLE_COLOR_BUTTON = 'closebutton';
STYLE_TEXT = 'text';
STYLE_TABITEM = 'tabitemstyle';
private var
FCloseBtn: TCustomButton;
FTabControl2: TTabControl;
FOnClose: TCloseEvent;
protected
procedure ChangeParent; override;
procedure ApplyStyle; override;
procedure FreeStyle; override;
procedure DoCloseBtnClick(Sender: TObject);
function DoSetWidth(
var ioValue: Single;
iNewValue: single;
var ioLastValue: Single): boolean; override;
public
class function Make(const iParent: TTabControl): TTabItemWithClose;
property TabControl: TTabControl read FTabControl2;
published
property OnClose: TCloseEvent read FOnClose write FOnClose;
end;
一番最初の「[ComponentPlatformsAttribute」はこのコンポーネントがどのプラットフォームで動作するのかを示すモノです。
ここでは、Win32, Win64, OSX32 で動作するとしています。

あとは、閉じるボタンが押されたときのイベントとか、必要な変数・メソッドを定義しています。

ちなみに、StyleLookup をクリックすると出てくるデフォルトの Style は TStyledControl.GetDefaultStyleLookupName で取得します。
なので、ここを Override すると、デフォルトの名前も変わります。
今回の TTabItemWithClose は GetDefaultStyleLookupName を Override しないので、

TabItemWithCloseStyle

という名前のスタイルがデフォルトになり、このスタイルを StyleBook から探すようになります。
なので、先ほど作った Style の名前を TabItemWithCloseStyle とする必要があります。

それでは、まず、DoSetWidth というメソッドを見てみます。
ここを修正すると Tab の大きさを変更できます。

function TTabItemWithClose.DoSetWidth(var ioValue: Single; iNewValue: single;
var ioLastValue: Single): boolean;
begin
if (FCloseBtn <> nil) then
iNewValue := iNewValue + FCloseBtn.Width * 1.5;
 
Result := inherited;
end;

このように変更すると CloseButton の幅の 1.5 倍が足された幅が新しい Tab の幅になります。
それでは、CloseButton はどこで取得するかというと ApplyStyle で取得します。

procedure TTabItemWithClose.ApplyStyle;
var
CloseBtn: TFmxObject;
begin
inherited;
 
CloseBtn := FindStyleResource(STYLE_COLOR_BUTTON);
if (CloseBtn <> nil) and (CloseBtn is TCustomButton) then begin
FCloseBtn := TCustomButton(CloseBtn);
FCloseBtn.OnClick := DoCloseBtnClick;
end;
end;

ApplyStyle は、スタイルを適用するときに呼ばれるメソッドです。
そして、FindStyleResource を使うことで、指定したスタイルを「コントロールとして」取得できます。
なので、ソースのように TCustomButton にキャストしてやって、閉じるボタンのインスタンスを保存します。

あとは、FreeStyle メソッドが呼ばれると Style が無効になるので、ここで FCloseButton に nil を代入しています。
また、initialization 部でコンポーネントを登録します。

initialization
begin
RegisterFmxClasses([TTabItemWithClose], [TTabControl]);
end;

登録しないと、IDE で作成された TTabItemWithClose が起動時やプロジェクト読み込み時にエラーになります。

基本的には、これでできあがりです。

他のメソッドは、僕が Style を一々作り直すのがメンドクサイので、そのための機構が入っています。
これについては、ソースをご覧ください。

そして、最後に、コンポーネントエディタを作らなくてはなりません。
作らないと、IDE で編集すると、TTabItemWithClose は作成されず TTabItem しか作成されません!

ということで、コンポーネントエディタを作りますが……これも DEKO さんが Advent Calendar で発表されているので、そちらをご覧ください!
僕が書くより、よっぽど詳しいです!!
コンポーネントとして登録する方法なども、リンクがあります!

では、できたコンポーネントが登録されたとして、これを使うためには、先ほど保存した TabItemWithCloseStyle を StyleBook に「追加」を押して、読み込ませます。



それで、適用して閉じたあと、コンポーネントエディタから TTabItemWithClose を作ると……



できました!!!

ちょっと駆け足になったり、はしょったりしましたが、こんな感じで FireMonkey のコンポーネントを作り出せます!
また、記事にすると長いですが、このコンポーネントを作るのに、実質1時間も掛かっていません!

簡単に作れるので、みなさんも、是非 FireMonkey でコンポーネントを作ってみて下さい!

0 件のコメント:

コメントを投稿