タイトル小池邦人のプログラミング日記》2001/4/22<ウィンドウのLive Resizing その2>カテゴリーCarbon/CF, 小池邦人のプログラミング日記
作成日2001/4/23 16:39:58作成者新居雅行
今回は前回の続きです。「ウィンドウのLive Resizing」を実現しているCarbon Event Handler(Carbon Event処理ルーチン)のソースコードを、より良い形へと書き直しててみます。

まずは、おさらいからです。前回作成したCarbon Event Handlerのソースコードをもう一度見てみます。

 

このソースの「美しくない点」は2箇所ありました。ひとつ目は、ウィンドウのサイズオーバーをCarbon Eventの処理内で逐次チェックしている点です。ViewJPEGアプリのウィンドウには画像が表示されています。マウスドラッグでウィンドウを拡大したとしても、それが画像矩形枠より大きくなることは許されません。画像ウィンドの最大矩形枠(画像矩形枠に左右のスクロールバーを加えたサイズ)はgetGrowRect()ルーチンで求めるられます。

 

GetEventParameter()で現在のウィンドウ矩形枠を得て最大矩形枠とSectRect()を行い、制限内に内に収めるように対処しています。もうひとつは、ウィンドウの画像を再描画する前にSizeWindow()を呼び出している点です。このルーチンを呼ばないと、最後にマウスカーソルが静止した位置での中身の再描画が行われないことは前回に説明しました。元来、SizeWindow()はシステムで実行されているはずですので、ここで呼び出さなければいけないのは釈然としません。

新しく改訂されれたCarbon Eventsに関するPDFドキュメントを調べてみると、10ページから「Default Event Handlers」という章があります。Carbon Event Modelでは、開発者側がウィンドウに対するCarbon Event Handlerを何も用意していないと、システムが用意しているHandlerが利用される仕組みになっています。つまり、システム側が自動でCarbon Eventの世話をしてくれるわけです。これを、Default Event Handlerと呼びます。こうしたDefault Event Handlerは、ユーザの操作対象である、あらゆるオブジェクト(アプリケーション、ウィンドウ、コントロール、メニューなど)に対して用意されており、開発者側の負担を減らすように考慮されています。ですから、昔のEvent Modelの場合なら、ウィンドウの移動、リサイズ、ズーム、閉じる処理などを実現するには、必ずWindow MangerのAPIを用いて、何らかのソースコードを記述しなければいけませんでした。しかしCarbon Event Modelでは、その手間の大部分を省くことが可能なのです。

この章で重要な箇所は、11ページから12ページにかけて記述されている、ウィンドウに関連したDefault Event Handlerの挙動内容です。マウスドラッグによりウィンドウのリサイズが起こった時、いくつものCarbon Event(Event Kind)が発生します。ここでは、その発生順序が時系列に解説されています。まずはユーザのマウスクリックにより「kEventClassMouse」(Event Class)の「kEventMouseDown」(Event Kind)が発生します。それ以降に発生するCarbon Eventを順次記載してみました(括弧内はEvent Kindです)。

(1)マウスがウィンドウのどの部分(パーツ)でクリックされたかチェックされる。
  「kEventWindowHitTest」

(2)ウィンドウのRisize Region(グロウボックス)でクリックされたと判断される。
  「kEventWindowClickResizeRgn」

(3)ウィンドウの最小サイズ(矩形枠)と最大サイズが設定される。
  「kEventWindowGetMinimumSize」「kEventWindowGetMaximumSize」

(4)以下の3ステップはマウスドラッグ中に繰り返して処理される。

 (a)マウスドラッグがなされている。「kEventMouseDragged」
 (b)ウィンドウサイズが変更される。「kEventWindowBoundsChanging」
 (c)ウィンドウ枠の描画を行う。「kEventWindowGetGrowImageRegion」

(5)ユーザによりマウスボタンが離される。
  「kEventMouseUp」

(6)サイズが変更されたウィンドウ自身が再描画される。
  「kEventWindowDrawFrame」

(7)最終的なウィンドウ枠が決定される。
  「kEventWindowBoundsChanged」

(8)ウィンドウのアップデートが要求される。
  「kEventWindowUpdate」

(9)ウィンドウの中身がの再描画が要求される。
  「kEventWindowDrawContent」

上記の流れを見てみると、ステップ3で呼び出されるkEventWindowGetMinimumSizeとkEventWindowGetMaximumSizeをこちら側で処理すれば、リサイズ最中に逐次ウィンドウサイズをチェックする必要はなさそうです。ViewJPEGアプリのHandlerルーチンに両Event Kindに対応する処理を追加することで、まずは最初の問題点は解決しました。

 

もうひとつの問題点は、kEventWindowBoundsChangedというEvent Kindが用意されていることに気づき謎が解けました。どうもリサイズ終了時のウィンドウサイズは、このEvent Kindが送られきた時点で決定されているようです。よって、ソースコードを以下のように書き直すことで、SizeWindow()を実行しなくても問題なくLive Resizingを行うことが可能になりました。

 

ちなみに、Mac OS XでウィンドウアトリビュートにkWindowLiveResizeAttributeをセットした時には、(4)の(c)は発生せず、代わりに(6)での処理が逐次実行されていると考えられます。それ以外にも、Mac OS XとMac OS 9では、Carbon Eventの発生順序や種類に微妙な違いがあるようです。よって、Event Handlerをインストールする時点で、前回紹介したchkSysOSX()を利用し、Mac OSのバージョンによりインストールすべきEvent Kindを切り替えることにしました。

 

今回の場合、Mac OS Xでは「kEventWindowBoundsChanging」と「kEventWindowBoundsChanged」がインストールされていますが、逆に「kEventWindowResizeCompleted」はMac OS 9のみのインストールとなります。こうした個別対処は、メニューやコントロールに対するCarbon Event Handlerのインストールでも必要になるかもしれません。

また、Carbon Eventには、名前は似ているが発生するタイミングが微妙に違う種類がいくつかあるようです。例えば、「kEventWindowClose」と「kEventWindowClosed」という似たような名前のEvent Kindがあります。前者はウィンドウのクローズボックスがクリックされ、マウスボタンが離された時点で発生します。後者はウィンドウが閉じられてしまってから発生します。最初のうち、ViewJPEGアプリでの画像ウィンドウを閉じてメモリを解放する処理は、kEventWindowClosedのHandlerルーチンとしてインストールされていました。しかし、これは大きな間違いです。これではウィンドウがすでに閉じられてしまった(DisposeWindow()が実行された)後に、またウィンドウを閉じる処理を繰り返えすことになり、大きなバグを生み出していたことになります。

Universal InterfacesのCarbonEvent.hには、個々のEvent Kindの内容や、その時点で入出力できるパラメータの種類については、そこそこ詳しい解説が載っています。しかし、Default Event Handlerを自分自身のHandlerルーチンに書き換えるような場合には、ユーザのマウスやキーボード操作により、どのような時系列でどんな種類のEvent Kindが発生するのかという情報が大変重要になります。残念ならら、こうした情報に関しては、まったく手がかりがありません。よって開発者側が自分でかってに想像するしかないのですが、それは危険で非効率な作業です。デベロッパーにもっとCarbon Event Modelを利用してもらいたければ、Apple社は、さらなる技術情報をドキュメントやサンプルソースとして早急に提供する必要があるでしょう。

今回の解説に用いたViewJPEG v1.23のソースコードは、以下のサイトに登録されています。詳細を確認したい方は、ぜひダウンロードしてみてください。

◇Ottimo: Digital Library
 http://www.ottimo.co.jp/library/index.html

[小池邦人/オッティモ]
関連リンクオッティモ