![]() ![]() ![]()
|
SophiaFramework 2.2 |
SophiaFramework ではウインドウやボタンなどの UI コンポーネントをレスポンダと呼んでいます。 それぞれのレスポンダには、イベント ハンドラと描画ルーチンが用意されています。 レスポンダのイベント ハンドラと描画ルーチンのインターフェースは 抽象的に設計されているので、レスポンダ単位でソース コードを再利用することが 容易になります。例えば、レスポンダの設計に従えば、プロジェクト1で 作成したウィンドウをプロジェクト2で、ほぼそのまま使用することが できるようになります。
レスポンダ モデルは大きく分けると、 SFRApplication、 SFRWindow、 SFRMenu、 SFRControl という 4 つの UI コンポーネント基底クラスから構成されます。 レスポンダは、アプリケーションがウィンドウを管理し、ウィンドウはコントロールを 管理しと、階層構造となっています。 SFRApplication はすべてのレスポンダの所有関係の頂点に位置するレスポンダであり、 アプレット一つに付き必ず一つのみ必要です。 通常は SFRApplication クラスを継承したクラスとしてプログラムを書き始めます。
SophiaFramework では、アプリケーションは UI コンポーネントと 同等に扱われるので、ウインドウやメニューは SFRApplication クラスに 貼り付けて使用します。またそれらは、任意の数だけ作成することが 可能です。 コントロールはウィンドウに貼り付けて使用され、こちらもユーザーが 任意の数だけ作成することが可能です。
その他に SFRWindow の拡張として SFRDialog が存在します。 ダイアログはウィンドウとほとんど同じ挙動をしますが、 ダイアログ特有のイベント ハンドラがいくつか登録されています。
ここでは、レスポンダの詳細について解説します。
レスポンダにはタイプという 4文字リテラル の識別子があります。 タイプは、アプリケーションやウィンドウなどを区別するために 使用されます。タイプはユーザーが設定することはできません。 ただし、SFRResponder クラスを継承して新たなクラスを作成するときのみ設定可能です。
タイプを設定するときは、4文字リテラルを使用してください。 4文字リテラルを作成するには FOUR_CHAR_CODE マクロを使用します。
![]() |
注意 |
|---|---|
すべて小文字のタイプは SophiaFramework で予約されています。使用してはいけません。 | |
タイプを取得するには SFRResponder::GetType 関数を使用します。
レスポンダにはアトリビュートという4文字リテラルの識別子があります。 アトリビュートとは、オブジェクトの種類を表し、ウィンドウではタイトル付きや タイトルなし、コントロールでは各コントロールの種類を表しています。 アトリビュートはオブジェクト生成時にユーザーが独自に オーバーライトすることが可能です。 オーバーライトすることで検索関数を使うときにより便利になります。
アトリビュートを設定するときは、4文字リテラルを使用してください。 4文字リテラルを作成するには、FOUR_CHAR_CODE マクロを使用します。
![]() |
注意 |
|---|---|
すべて小文字のアトリビュートは SophiaFramework で予約 されています。使用してはいけません。 | |
アトリビュートを取得するには SFRResponder::GetAttribute 関数を使用します。
レスポンダにはレスポンダの状態や見た目や属性を管理するフラグが 存在します。レスポンダの状態とは可視であるか不可視であるかなどであり ユーザーの操作にしたがって変化します。 属性とは透明かどうかや、移動可能かどうかなどであり ユーザーの操作によって変化することはありません。 レスポンダの状態は生成時に指定することや、生成後に関数を使用して 設定することもできます。見た目と属性は生成時にのみ指定可能です。 レスポンダの振る舞いは以下の定数で設定できます。
状態:
BEHAVIOR_NONE STATUS_VISIBLE STATUS_ENABLE STATUS_FOCUS STATUS_TARGET
STATUS_VISIBLE フラグは、可視状態であるか不可視状態で あるかを判断するためのフラグです。 標準状態ではこのフラグが設定されているときのみ オブジェクトは描画されます。
STATUS_ENABLE フラグは、応答可能か応答不可能かを 判断するためのフラグです。 標準状態ではこのフラグが設定されているときのみ オブジェクトはキー イベントやその他一部のイベントに応答します。 またフォーカス可能かどうかも STATUS_ENABLE フラグにより 決定されます。
STATUS_FOCUS フラグはフォーカスが当たっているかそうでないか を判断するためのフラグです。 ターゲット可能かどうかも STATUS_FOCUS フラグにより決定されます。
STATUS_TARGET フラグはターゲッティングされているかどうかを 判断するフラグです。 標準状態では、キー イベントやその他一部のイベントは ターゲッティングされているときに受け取ることができます。 PROPERTY_DIRECT フラグが設定されているときは、常に STATUS_TARGET フラグは 設定されています。
見た目:
APPEARANCE_TRANSPARENT
APPEARANCE_TRANSPARENT フラグはコンポーネントが透過領域を持っているかどうか を判断するためのフラグです。チェック ボックスやラジオ ボタンなど 一部のコントロールで使用されていますが、ウィンドウに使用することも 可能です。 このフラグが設定されているとレンダリング時の処理内容が透過領域を 意識したアルゴリズムに変更されます。
![]() |
警告 |
|---|---|
透過レンダリングは、コントロールの一部で使用することを 目的に作成されています。ウィンドウやその他のコンポーネントに 使用することも可能ですが、レンダリング時の処理が非常に 重くなります。 | |
属性:
PROPERTY_DIRECT PROPERTY_SELECT PROPERTY_CLOSABLE PROPERTY_MOVABLE PROPERTY_SCROLLABLE
PROPERTY_DIRECT フラグはターゲッティングを行わなくてもイベントを 受け取れるかどうかを判断するフラグです。このフラグが設定されていると STATUS_TARGET フラグは常に設定されることになり、また関数を使用して STATUS_TARGET フラグをクリアすることもできなくなります。 コントロールなど一部のコンポーネントでは必ずこのフラグが設定されます。 ターゲッティングについては、以下の図を参照してください。
PROPERTY_SELECT フラグは背後にあるオブジェクトをターゲッティングしたときに 自動的に最前面に移動させるかどうかを判断するためのフラグです。 ウィンドウなど一部のコンポーネントは必ずこのフラグが設定されます。
PROPERTY_CLOSABLE フラグはクリア キーでオブジェクトを破棄できるか どうかを判断するためのフラグです。 標準状態ではすべてのコンポーネントは破棄不能となっています。
PROPERTY_MOVABLE フラグはユーザーがオブジェクトを移動可能かどうかを 判断するためのフラグです。Move 関数や GroupMove 関数を使用してオブジェクトを 移動させる場合にはこのフラグは適用されません。 標準状態ではすべてのコンポーネントは移動不可となっています。
PROPERTY_SCROLLABLE フラグはユーザーが仮想領域をスクロール可能かどうかを 判断するためのフラグです。Scroll 関数や GroupScroll 関数を使用して オブジェクトを移動させる場合はこのフラグは適用されません。 標準状態では SFRApplication 以外のコンポーネントはスクロール不可となっています。
SophiaFramework での状態の遷移は以下のように定義されています。
STATUS_VISIBLE が設定されているとき、STATUS_ENABLE が有効。 STATUS_ENABLE が設定されているとき、STATUS_FOCUS が有効。 STATUS_FOCUS が設定されているとき、STATUS_TARGET が有効。 STATUS_TARGET が設定されているとき、操作対象としてロック。
また、PROPERTY_DIRECT が設定されているときは、常に STATUS_TARGET は設定された 状態となります。
PROPERTY_CLOSABLE と PROPERTY_MOVABLE は STATUS_TARGET が設定されていないとき 有効です。また、PROPERTY_SELECT は背後にあるレスポンダを STATUS_TARGET 状態に するときに自動的に最前面に移動させるかどうかを判断します。
レスポンダには描画のための領域やレスポンダの位置を表す 領域など3つの領域があります。
ベース領域. レスポンダの位置を表します。 親レスポンダの仮想領域上での座標を指定します。
コンテント領域. 仮想領域のうち実際に描画される領域を表します。 この領域は、ベース領域よりも大きくなることはありません。
フレーム領域. ベース領域とコンテント領域の間の領域です。 ベース領域とコンテント領域が同じときは、フレーム領域は 存在しません。
仮想領域. 描画可能な仮想的な領域を表します。この領域は 実際に目に見える領域よりも大きくてもかまいません。
レスポンダ モデルでは UI コンポーネントを管理する方法として タイプとアトリビュートと状態をキーにして目的のオブジェクトを検索する システムが搭載されています。このシステムを使うと、ウィンドウや コントロールのポインタをどこかに保持する必要がなくなり プログラムをより抽象的に記述できるようになります。
検索方法には以下の方法があります。
| 1:最前面にある物を取得(タイプ・アトリビュート・状態が一致) |
| 2:最背面にある物を取得(タイプ・アトリビュート・状態が一致) |
| 3:前面からN番目を取得(タイプ・アトリビュート・状態が一致) |
| 4:背面からN番目を取得(タイプ・アトリビュート・状態が一致) |
| 5:次を取得(タイプ・アトリビュート・状態が一致) |
| 6:前を取得(タイプ・アトリビュート・状態が一致) |
| 7:グループ化された物の次を取得(タイプ・アトリビュート・状態が一致) |
| 8:グループ化された物の前を取得(タイプ・アトリビュート・状態が一致) |
またタイプやアトリビュートはワイルドカードも使えます。
検索関数のうち上記項目の1・2・3・4は自分の子階層のオブジェクトを 検索します。また、項目5・6・7・8は自分と同階層のオブジェクトを 検索します。
可視かつ応答可能なウィンドウのうち最前面にあるウィンドウを 取得するには以下のように記述します。
SFRResponderPtr responder; SFRWindowPtr window; window = SFRWindowPtr(responder->GetFront(TYPE_WINDOW));
可視かつ応答可能なタイトル ウィンドウのうち最前面にあるウィンドウを 取得するには以下のように記述します。
SFRResponderPtr responder; SFRWindowPtr window; window = SFRWindowPtr(responder->GetFront(TYPE_WINDOW, ATTRIBUTE_TITLEWINDOW));
可視・不可視や応答可能などを無視してすべてのウィンドウのうち 最前面にあるウィンドウを取得するには以下のように記述します。
SFRResponderPtr responder; SFRWindowPtr window; window = SFRWindowPtr(responder->GetFront(TYPE_WINDOW, ATTRIBUTE_WILDCARD, BEHAVIOR_NONE));
可視かつ応答可能なウィンドウを最前面から最背面までたどるには以下の ように記述します。
SFRResponderPtr responder;
SFRWindowPtr window, temp;
if ((window = SFRWindowPtr(responder->GetFront(TYPE_WINDOW))) != NULL) {
// first window
temp = window;
while ((temp = SFRWindowPtr(temp->GetNext(TYPE_WINDOW))) != NULL && temp != window) {
// second window or ...
}
}
可視かつ応答可能なタイトル ウィンドウの中で前面から3番目のウィンドウを 取得するには以下のように記述します。
SFRResponderPtr responder; SFRWindowPtr window; window = SFRWindowPtr(responder->GetNth(2, TYPE_WINDOW, ATTRIBUTE_TITLEWINDOW));
![]() |
Tip |
|---|---|
最後の引数の状態フラグが表す条件は一致ではありません。 設定フラグが検索項目に追加されることを意味しています。 つまり、BEHAVIOR_NONE = 0 を設定すると状態に関しては 何も条件に含めません。STATUS_VISIBLE を設定すると 可視・不可視項目のみ条件に含めることになりそれ以外の 状態フラグは無視されます。 また設定できるフラグは STATUS_VISIBLE から STATUS_TARGET までで それ以外のフラグは設定しても無視されます。ただし、 将来検索項目に含まれる可能性があるためなるべく必要以外の フラグを設定することは避けることを推奨します。 | |
レスポンダ モデルでは、イベント ハンドリングを自動的に行います。 イベント ハンドリングではトレーサとハンドラを使用することになりますので これらを正しく使いこなすことが重要になります。
トレーサは特定のイベントがどのようにそれぞれの UI コンポーネントに 通知されるかの規則を保持しています。厳密に言えばどのように子階層の オブジェクトに通知されるかということです。トレーサは親の階層から継承され 子階層でオーバーライトすることもできます。たとえば、SFEVT_KEY などは フォーカスの当たっているオブジェクトを順にたどっていくという規則が 設定されています。また、SFEVT_RESUME などはすべてのオブジェクトに 通知されるという規則が設定されています。
トレーサの規則には以下の要素を設定できます。
| 1:イベント タイプと第1パラメータ |
| 2:トレース順序(子階層への通知なし・フォーカスへ・後ろから前へ・前から後ろへ) |
| 3:強制通知 |
| 4:状態フィルタ |
イベント タイプと第一パラメータは BREW イベントのそれと同じです。 一致するイベントへのトレーサとなります。トレース順序は子階層へ イベントを通知するときの規則です。強制通知は自分と子階層の オブジェクトのうちあるオブジェクトがイベントを処理したとしても 強制的にイベントを通知し続けるかしないかのフラグです。SFEVT_KEY などは、 通常の用途ではどれか一つのオブジェクトがイベントを処理した場合は 通知を続ける必要は無いため強制通知フラグは設定されていません。 SFEVT_RESUME などはイベントの通知を打ち切ってしまうと不都合なので 強制通知フラグが設定されています。 状態フィルタは、可視状態や応答可能状態など特定の状態のときのみ イベントを受け取れるようにするためのフィルタです。
トレーサは親階層から継承されるために、デフォルトのトレーサは ほとんどがアプリケーション階層に登録されています。 メニュー イベントやコントロール イベントなどのトレーサは メニューやコントロールに登録されています。 一部の規則を変更したい場合は、ユーザーがトレーサを登録することによって デフォルトの規則を変更できます。
標準では以下のようになっています。
表 10.1.
| 1: |
|
||||
| 2: |
|
||||
| 3: |
|
||||
| 4: |
|
||||
| 5: |
|
||||
| 6: |
|
||||
| 7: |
|
||||
| 8: |
|
||||
| 9: |
|
||||
| 10: |
|
||||
| 11.1: |
|
||||
| 11.2: |
|
||||
| 12: |
|
||||
| 13: |
|
||||
| 14: |
|
ハンドラは特定のイベントを処理する関数を保持しています。
ハンドラの規則には以下の要素を設定できます。
| 1:イベント タイプと第一パラメータ |
| 2:処理のタイミング(子階層への通知の前・後) |
イベント タイプと第一パラメータは BREW イベントのそれと同じです。 処理のタイミングは HANDLER_BEFORE と HANDLER_AFTER を使用して、子階層へ イベントを通知する前か後かを設定できます。 SFEVT_KEY などは普通フォーカスの当たっている子階層から イベント処理を開始することが多いために、 処理のタイミングは後となります。実際にイベントがどのようにハンドリング されるかは以下の図を参考にしてください。
ハンドラは標準でいくつか定義してありますが、 ユーザーが独自に定義して登録することも可能です。 またハンドラは親クラスで登録したものをオーバーライトすることも可能です。
ハンドラは次の型をもつ静的メンバ関数でなければなりません。
Bool (*SFRResponderSPP) (ConstSFUEventRef event, VoidPtr reference);
ここではハンドラの登録をするためのサンプル コードを示します。 ハンドラを登録するには RegisterHandler を、登録されているハンドラを 破棄するには UnregisterHandler を使用してください。 登録するときに、すでに同じイベントに対するハンドラが登録 されていたときはオーバーライトされます。 RegisterHandler と UnregisterHandler は数多くのオーバーロード版 の関数が用意されているので使いやすくなっています。
SREVT_CONTROL イベントに対するハンドラを子階層よりも前に処理する という規則で登録するには以下のように記述します。
SFRResponderPtr responder; SFCError error; error = responder->RegisterHandler(SREVT_CONTROL, HANDLER_BEFORE, Control_static, this);
SFEVT_KEY から SFEVT_KEY_HELD までのイベント タイプでかつ 第一パラメータが AVK_SELECT のイベントに対するハンドラを 子階層よりも後に処理するという規則で登録するには以下のように記述します。
SFRResponderPtr responder; SFCError error; error = responder->RegisterHandler(SFEVT_KEY, SFEVT_KEY_HELD, AVK_SELECT, HANDLER_AFTER, Key_static, this);
ハンドラは静的メンバ関数で登録しなければならないため、 通常は以下のようの使用方法が多くなると思われます。
inline SomeClass::SomeClass(Void)
{
RegisterHandler(SFEVT_KEY, HANDLER_AFTER, Key_static, this);
return;
}
static inline Bool Key_static(ConstSFUEventRef event, VoidPtr responder)
{
return(SomeClassPtr(responder)->Key_handler(event));
}
inline Bool Key_handler(ConstSFUEventRef event)
{
// something to do
return(TRUE);
}
特定のイベントにおいては具体的なハンドリングはしないが、 処理したことにして TRUE を返したいことがときどきあります。 このようなときは TRUE を返すだけの関数を定義して それを登録する方法もありますが、コールバック エントリ を NULL で登録すれば効率的です。 コールバック エントリが NULL のときは具体的なハンドラは 呼び出されませんがイベントは処理されたことになります。
キーに関するイベントをすべてハンドルして無効にするには 以下のように記述します。
SFRResponderPtr responder; SFCError error; error = responder->RegisterHandler(SFEVT_KEY, SFEVT_KEY_HELD, HANDLER_BEFORE, NULL, NULL);
![]() |
警告 |
|---|---|
上記コードでキー イベントを本当に無効にすることができるかは トレーサの設定にも依存していることに注意してください。 | |
ハンドラは新しく登録されたものほど有効です。 たとえば、以下のそれぞれのサンプルの挙動は違うことに注意してください。
SFRResponderPtr responder; responder->RegisterHandler(SFEVT_KEY, HANDLER_AFTER, Key_static, this); responder->RegisterHandler(SFEVT_KEY, SFEVT_KEY_HELD, HANDLER_AFTER, Held1_static, this); responder->RegisterHandler(SFEVT_KEY, SFEVT_KEY_HELD, HANDLER_AFTER, Held2_static, this);
SFRResponderPtr responder; responder->RegisterHandler(SFEVT_KEY, SFEVT_KEY_HELD, HANDLER_AFTER, Held1_static, this); responder->RegisterHandler(SFEVT_KEY, SFEVT_KEY_HELD, HANDLER_AFTER, Held2_static, this); responder->RegisterHandler(SFEVT_KEY, HANDLER_AFTER, Key_static, this);
トレーサとハンドラどちらも登録と破棄ができます。 またイベント タイプと第一パラメータは範囲指定で設定 することができます。たとえばイベント タイプが SFEVT_KEY から SFEVT_KEY_HELD までで AVK_SELECT を第1パラメータに持つイベントに 対して設定したり、イベント タイプが SFEVT_KEY で第一パラメータ が AVK_1 から AVK_9 までを持つものに対して設定したりできます。 どちらの登録時も親クラスで登録した規則をオーバーライトすることが 可能です。
レスポンダ モデルでは再描画領域はスケジューリングされ、イベント ループの 出口で一度だけレンダラが起動され再描画が行われます。再描画領域は長方形領域として 登録することが可能であり、2つの長方形領域を登録した場合は両方の 領域を含むより大きな長方形領域とみなされます。
再描画開始前に、スケジューリングされている領域は整理されて 不可視となっているオブジェクトや他のオブジェクトの背後に隠れて 見えないオブジェクトのスケジューリングは解除されます。 また、画面外にあって見えない領域なども除外されます。 すべてのスケジューリングを整理した後に、それぞれのオブジェクトの 描画ルーチンは一度だけ実行されます。
レスポンダ モデルではそれぞれのオブジェクトはオフスクリーンを保持している わけではないために、描画ルーチンは基本的に毎回すべての描画を 行わなくてはなりません。ただし、不必要な領域はあらかじめクリッピング されており描画範囲には入らないようになっています。
強制描画を行いたい場合は対象のオブジェクトの再描画必要範囲を スケジューリングしてアプリケーションに対して再描画イベントを 送信します。
SFRApplicationPtr app; SFRResponderPtr responder; responder.InvalidateContent(SFURect(0,0,30,30)); app->Invoke(SFUEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, TRUE));
![]() |
警告 |
|---|---|
アプリケーション以外にも再描画イベントを送信することは できますが、動作は保証されません。 | |
ここではアップデート領域を登録するためのサンプル コードを示します。
アップデート領域の登録は InvalidateBase もしくは InvalidateContent を使用して 行います。InvalidateBase はコンテント領域の外側かつベース領域の 内側、つまりフレーム領域でアップデートが必要になったときに使用します。 また、InvalidateContent はコンテント領域内でアップデートが必要になったときに 使用します。一般的なアプレット開発では InvalidateContent を使用する頻度のほうが 高いと思われます。
コンテント領域内の領域を再描画登録するには、以下のように記述します。
SFRResponderPtr responder; SFURect rect; rect.set(0, 0, 10, 10); // example responder->InvalidateContent(rect);
ベース領域内で発生したアップデート領域を登録するには以下のように記述します。
SFRResponderPtr responder; SFURect rect; rect.set(0, 0, 10, 10); // example responder->InvalidateBase(rect);
レスポンダ モデルではレスポンダの破棄に delete 演算子を 使用してはいけません。レスポンダを破棄するには、そのレスポンダに対して SREVT_RESPONDER_TERMINATE イベントを送信しなければなりません。 この規則の例外として、コンストラクタ内でエラーが発生した場合は、 delete 演算子を使用して破棄してもかまいません。
レスポンダを生成して、すぐに破棄するサンプル コードを以下に示します。
SFRWindowPtr window;
if ((window = new SFRTitleWindow(this, SFURect(0, 0, 100, 100), "Sample")) != NULL) {
if (window->Exception() == NO_ERROR) {
window->Invoke(SFUEvent(SREVT_RESPONDER_TERMINATE, SRP16_TERMINATE_INVOKE, TRUE));
}
else {
delete window;
}
}
上記コードでは、window->Exception() がエラーを返した場合 delete 演算子を用いてオブジェクトを破棄しています。 一度生成が成功した後は、window->Invoke() を使用して破棄イベントを 送信して破棄しなければなりません。 生成成功後に delete 演算子を用いて破棄した場合の 動作は保証されません。
また、破棄に関連して、レスポンダを継承したクラスのデストラクタは必ず virtual でないといけません。
| Copyright(C) 2003-2004 Sophia Cradle Inc., All Rights Reserved. |
![]() ![]() ![]()
|