● 小池邦人のMac OS Xへの道 2002/04/09

〜 オブジェクトのセレクションを実現する その1 〜

今回は、オブジェクトのセレクションにOverlayウィンドウを利用する話です。Mac OS Xでの「流行」は、マウスドラッグで得た選択エリアを、半透明の矩形として表示する方法です。半透明の矩形を描画するのには、Quartz 2D APIを利用することになります。

マウスドラッグで、アイコン、セル、画像などの「オブジェクト」を選択(セレクト)する作業は、Macintoshのアプリケーションにとっては、ごく普通のことです。Mac OS 8/9の時代には、マウスドラッグで得られた選択領域をグレーや波線矩形で表示するのが、オブジェクトを選択する一般的な手法でした。例えば、ペイントアプリケーションで用いられていた波線による選択方法は、Macintoshらしいユーザインターフェースの代名詞だったのではないでしょうか?

 

最初にMacPaintを操作した時、選択が終了しているのにもかかわらず、その領域がアニメーションとして動いているのを見て、ずいぶんと衝撃を受けたことを憶えています。そうそう、昔々、その波線ことを「魅惑のウニウニ波線」と呼び、プログラミングによる実現方法を「MacJapan」に紹介したこともありました(笑)。

ペイントアプリケーションで使われる「画像領域の選択」は、別の機会にチャレンジするとして、今回は、「Selection_Demo」というアプリケーションで、Mac OS XのFinderが行うアイコン選択の「雰囲気」を味わってみることにします。

 

まずは、Interface Builderでメインウィンドウを含んだNibファイルを作成しておきます。ウィンドウには、選択対象(ターゲット)として、アイコンを表示したBevelボタン・コントロールを25個並べておきます。

 

これをマウスドラッグで選択し、選択領域に接しているコントロールのみをリアルタイムでクリック状態(少し暗い表示)に切り換えます。Infoウィンドウで、Bevelボタン・コントロールのSignaitureに'cntl'を、ID番号に1から25を設定しておきます。つまり、最初のBevelボタン・コントロールはID=1で、最後はID=25となるわけです。

 

加えて、ウィンドウの下に配置されるボタンコントロールに対応するコマンドIDも設定しておきます。「全選択」ボタンには'sall'が、「選択解除」ボタンには'selc'が、「終了」ボタンには'quit'が割り付けられています。

 

「全選択」ボタンの'sall'は、編集メニューの「Select All」と同じコマンドIDとなります。「全選択」ボタンをクリックすると、すべてのBevelボタン・コントロールが選択されます。逆に「選択解除」ボタンをクリックすると、すべての選択が解除されます。「終了」ボタンをクリックすると、アプリケーションは終了します。

それでは、サンプルアプリケーションのソースコードを見てみます。まずは、メニューバー(MenuBar)とメインウィンドウ(MainWindow)をNibファイル(main.nib)から読み込みます。続いてsetUpWindowEvent()でメインウィンドウにCarbon Event Handlerルーチンをインストールしています。



SetWindowGroup()によりウィンドウを特定グループに属するように調整していますが、この処理はアバウト用のSheetウィンドウのためで、今回の選択機能とは関係ありません。本サンプルアプリケーションのアバウト表示とウィンドウグループ化の仕組みについては、以前紹介した「Group_Demo1」サンプルとまったく同じですので、そちらを参照してみてください。

以前と少し違う箇所は、setUpWindowEvent()の引数が拡張されており、登録されるEventTypeSpec構造体の数を切り換えられることです。引数で渡された「個数パラメータ」は、そのままInstallWindowEventHandler()に代入されています。



メインウィンドウでは、マウスクリックとその位置を認識するために、イベントクラスが「kEventClassWindow」でイベント種類が「 kEventWindowClickContentRgn」のCarbon Eventを受け取る必要があります。逆に、アバウト用のSheetウィンドウでは、この種のCarbon Eventを処理する必要はありません。ですから、引数として渡す「個数パラメータ」をひとつ減らして2とします。

以前に紹介した、「Group_Demo1」や「Overlay_Demoア」サンプルアプリケーションでも、Overlayウィンドウを使用していました。Overlayウィンドウとは、Mac OS X 10.1から採用されたディフォルトで下地が透明なウィンドウのことです。下地が透明ということは、QuikDrawで図形を描画すると、それだけが画面上に浮き上がることになります。また、QuikDrawの代わりにQuartz 2Dで半透明な図形を描画すると、その下のウィンドウ内容が透けて見えます。今回実現するコントロールの選択にも、Overlayウィンドウのこの特徴をうまく利用します。マウスドラッグによるBevelボタン・コントロール選択の過程は、だいたい以下の手順となります。

(1)メインウィンドウとまったく同じ位置にOverlayウィンドウを作る
(2)Carbon Event Handlerルーチンでマウスクリック座標値を得る
(3)マウスクリック座標値をセレクションの担当ルーチンに渡す
(4)TrackMouseLocation()で継続してマウスドラッグ座標値を得る
(5)Quartz 2Dで半透明の矩形領域をOverlayウィンドウへ描画する
(6)選択矩形領域に接触しているコントロールの値を1に設定する
(7)選択矩形領域から外れているコントロールの値をゼロに設定する
(8)マウスドラッグが終了したらOverlayウィンドウも削除する

マウスクリックの度にOverlayウィンドウを作成するのは効率的ではないので、ウィンドウのグループ化機能を使い、Overlayウィンドウを表示しっぱなしにすれば良いのでは?と言う意見もあると思います。しかし試してみると、ウィンドウ処理に色々と不都合が生じ、それを回避するために煩雑な処理が必要となり、あまり現実的ではありません(興味ある方はチャレンジしてみてください)。OverlayウィンドウをHideWindow()しておき、使う時だけにShowWindow()するという手もあります。しかし、実際に試してみると、ウィンドウ作成にはほとんど時間を消費しないことが分かります。また、使用済みのウィンドウを削除すうことで実メモリーの圧迫も回避できます。よって今回は、上記のような手順を取ることにしました。

「Selection_Demo」サンプルアプリケーションでOverlayウィンドウを作成しているのが、openOverlayWindow()ルーチンです。



ウィンドウを作成するのにはCreatWindow()を使います。代入するウィンドウクラス(WindowClass)にはkOverlayWindowClassを選択してください。Overlayウィンドウのサイズと表示位置をメインウィンドウとまったく同じにするために、GetWindowBounds()でメインウィンドウの矩形情報を得ています。GetWindowBounds()は、ウィンドウの様々な部品の矩形領域を得るためのAPIです。今回は引数としてkWindowContentRgn定数を渡し、ウィンドウ内部の矩形領域を得ています。どのような部品の矩形領域を得ることができるのかは、Universal Interfacesの「MacWindows.c」に定義されていますので、参照してみてください。



ここで解説している「Selection_Demo」サンプルアプリケーションは、以下のサイトに登録されていますので確認してみてください。動作させるのには、Mac OS X 10.1と最新版のDeveloper Toolsが必要です。

http://www.ottimo.co.jp/library/

次回は、今回の話の続きとなります。「Selection_Demo」サンプルアプリケーションのCarbon Event Handlerルーチンや、Bevelボタン・コントロールの選択を担当しているルーチンについて解説したいと思います。


copyright 2002 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact Us: koike@ottimo.co.jp