Macintoshでは、独自のボタンやスライダーなど「カスタムコントロール」を作成する手段が昔から存在していました。もっとも古くからある方法は、CDEF(Control Definition Functions)リソースを作成する方法です。Mac OS XにCarbon Frameworkが導入されてからは、CDEFリソースを作成せず、コントロールの機能をダイレクトにソースコード内に記述する方法も導入されましたが、基本的な仕組みはCDEFとあまり変わりませんでした。
カスタムコントロールをさらに効率良く開発するために、Mac OS X 10.2からHIObjectという仕組みが導入されました。今までのCDEFですと、コントロールが持っているユーザインターフェースや表示機能をすべて独自に実装する必要がありました。しかし、HIObjectを利用すれば、カスタムコントロールを既存のコントロールのサブクラスとして定義できます。つまり、変更が必要な機能だけをオーバーライド(差分として追加)するだけでカスタムコントロールを作成できるようになったのです。
Mac OS X 10.3になり、この仕組みはより洗練されました。例えば、表示方法やユーザインターフェースは標準ボタンとまったく同じでよいが、ボタンクリックでタイトルを「喋る」ボタンを作成したいとします。その場合には、プッシュボタンクラスのサブクラスを作り、喋る機能(メソッド)のみをそこへ追加すれば良いことになります。今回は、カスタムコントロールの機能を一通り解説するために、カスタムボタンをkHIPushButtonClassのサブクラスではなく、kHIViewClassのサブクラスとして定義してみます。

まずは、独自サブクラス(kHIMyButtonClassIDとする)の名称を定義します。この名称、どのように記述しても良いような気もしますが(笑)何かしらルールがあるようでして、Apple社が用意している既存コントロールのCalssIDは"com.apple.HIXXXXX"といった名称が付けられています。続いてカスタムコントロールの情報を保存するための構造体を定義します。今回はメンバーがHIViewRefひとつだけのMyButton構造体を用意しました。
次に、Control ManagerのCreatePushButtonControl()やCreateCheckBoxControl()に相当するcreateMyButtonControl()ルーチンを作成します。コントロールを配置するウィンドウのWindowRefと配置位置のHIRectを指示することで、完成したカスタムコントロールのHIObjectRefを返してきます。このHIObjectRefは、HIViewRefやControlRefと同等の扱いです。また渡しているHIRectは、QuickDrawの矩形ではなくCore Graphics(Quartz 2D)の矩形に準拠していますので注意してください。

最初に、registMyButtonClass()でカスタムコントロールの機能を処理するCarbon Event Handlerをインストールします。続いてHIObjectCreate()でHIObjectRefを得ます。得られたHIObjectRefはHIViewRefつまりControlRefと同じなので、例えばそのコントロールを表示したい場合いには、ShowControl( (HIViewRef)hobj )を実行すれば良いわけです。続いてHIViewFindByID()でウィンドウのHIViewを探しHIViewAddSubview()でコントロールを貼り付けた後に、HIViewSetFrame()でその表示位置を決定します。

registMyButtonClass()は、独自クラスを上位クラスのサブクラスとしてレジストするためのルーチンです。具体的にはコントロールの機能を実装したCarbon Event HandlerをHIObjectRegisterSubclass()でインストールします。Handlerへ届くメッセージもCarbon Event準拠ですので、自分が必要とする機能に関連したイベントクラスと種類のペアをEventTypeSpecとして用意します。レジストは一番最初のコントロールが作成された時だけ実行されればOKです。よって、返されたHIObjectClassRefを外部変数のh_crefに保存し、2度目からは実行されないようにチェックしています。
myButtonHandler()ルーチンは、カスタムコントロールの挙動が記述されているCarbon Event Handler本体です。このルーチンで処理しているCarbon Eventのうち、HIObject特有のイベントは、クラスがkEventClassHIObjectで、種類がkEventHIObjectConstruct、kEventHIObjectDestruct、kEventHIObjectInitializeの3種類となります。コントロール作成時にはkEventHIObjectConstructが呼ばれます。MyButton構造体を確保し、その内容をSetEventParameter()に渡します。この処理により、次回にCarbon Eventが発生した時には、Handlerの引き数のinRefconからMyButton構造体を参照できるようになります。
kEventHIObjectDestructはコントロールが破棄される時に呼ばれます。確保したMyButton構造体を開放する処理を記述します。kEventHIObjectInitializeが呼ばれた場合にはコントロールの初期化を行います。今回のカスタムコントロールには特別な初期化処理は必要ありません。続いてCallNextEventHandler()を実行しているのは、スーパークラスの初期化ルーチンを呼び出して処理する機会を与えるためです。

クラスがkEventClassControlで、種類がkEventControlDraw、kEventControlHitTest、kEventControlHiliteChangedの3つのCarbon Eventは、それぞれ、カスタムコントロールの描画処理、ユーザによるマウスクリック、表示の更新要求に対し発生します。コントロールの描画では、外部変数に保存しておいたm_image(Core Graphics画像)を矩形内に表示します。もしm_imageが保存されていなければ黒い枠のみが表示されます。
コントロールがハイライト表示(クリック中など)の場合には、それが判別できるようにCGContextFillRect()により半透明の青色を画像の上に重ねます。ヒットテストではコントロールのどの部分がマウスクリックされたのかを判断し、その部分のID番号(今回はkControlButtonPart)を返します。表示の更新要求時ではHIViewSetNeedsDisplay()を呼び、表示内容が変更されたことをシステムに知らせます。
HIObjectについてはUniversal InterfacesのHIObject.hを参照してください。解説用コメント量が定義内容の5倍ぐらいあるヘッダファイルです(笑)。HIObjectやHIViewを利用したサンプルソースコードは、サンプルコードサイトの「Mac OS X HIToolbox」から入手できます。
http://developer.apple.com/samplecode/Sample_Code/Human_Interface_Toolbox/Mac_OS_X_HIToolbox.htm
copyright 2004 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact us: koike@ottimo.co.jp