前のページ次のページ上に戻るホーム SophiaFramework UNIVERSE 5.3

9.4. イベント処理

9.4.1. 概要

レスポンダのイベント処理は、以下のようにして行われます。

  1. ユーザー操作や外的要因により発生した割り込みが、 BREW イベントとしてアプリに送信されます。
  2. アプリが SFEVT_APP_START / SFEVT_APP_RESUME / SFEVT_APP_STOP / SFEVT_APP_SUSPEND イベントを受信したとき、 あるいは優先的イベントハンドラがイベントを処理しなかったとき、 SFYApplication::HandleEvent 関数が自動的に呼び出されます。
  3. SFYApplication::HandleEvent 関数は、 SFYResponder::Distribute 関数を呼び出して 配信エンジンを起動します。
  4. BREW イベントは、最初に SFYApplication クラスが内部で保持する ルートに関連付けられた 配信エンジン(SFYDistributer インスタンス)に配信されます。
  5. 次に トレーサの配信規則に基づいて ルート以下のレスポンダツリーに配信されます。
  6. BREW イベントを受信した SFYDistributer インスタンスやレスポンダは、 ハンドラを起動します。 ハンドラの処理によりレスポンダイベントやユーザーイベントを受信したレスポンダは、 そのハンドラを起動します。
  7. SFYResponder::Distribute 関数の result 引数に true が返された場合(1 つ以上のハンドラがイベントを処理して true を返した場合)、 または、SFEVT_APP_START(アプリ開始イベント)/ SFEVT_APP_RESUME(アプリ再開イベント)を受信した場合、 優先的イベントハンドラが登録されていなければ、 "SFYResponder::Render()" により 描画エンジンを起動して再描画を行います。
    [Note] 注意

    SFEVT_APP_END(アプリ終了イベント)/ SFEVT_APP_SUSPEND(アプリ中断イベント)を受信した場合や、 優先的イベントハンドラが登録されている場合、描画エンジンは起動されません。

  8. 配信エンジンや描画エンジンの起動中にメモリ不足などの致命的なエラーが発生した場合は、 SFCApplication::HandleError 関数を呼び出します。
  9. サスペンド時に描画エンジン内部のデバイス画面保存用ビットマップを解放する場合は、 描画エンジンを終了します(描画エンジンは次回再描画時に自動的に初期化されます)。
[Note] 優先的イベントハンドラについて

SFXEventBypass クラスの解説を参照してください。

[Caution] イベントの配信について

SFYResponder::Distribute 関数は、 この関数を呼び出した時点のレスポンダツリーに対してイベントを配信します。 イベントループ内で新規に作成されたレスポンダには配信しません。

また、イベントループ内の処理で SFYResponder::Terminate 関数により破棄されたレスポンダにも配信しません。

例 9.24. SFYApplication::HandleEvent 関数の内部実装

// アプリが BREW 環境からイベントを受信したときに呼び出される関数
/*protected virtual */Bool SFYApplication::HandleEvent(SFXEventConstRef event)
{
    SFCError  error;
    Bool      result(false);

    // 配信エンジンを起動してイベントを配信する
    // ※ イベントは最初にルートに関連付けられた SFYDistributer インスタンスに配信される
    //    その後、トレーサの配信規則に基づいてルート以下のレスポンダツリーに配信される
    if ((error = _root->Distribute(event, &result)) == SFERR_NO_ERROR) {
        // (_root はルート、result 引数にはイベントの処理結果が格納される)

        if (event.GetType() != SFEVT_APP_STOP & event.GetType() != SFEVT_APP_SUSPEND)) {  
            // 再描画が必要な場合

            if (IsRenderable()) {  // 優先的イベントハンドラが登録されていない場合

                // 描画エンジンを起動してルート以下のレスポンダツリーを再描画する
                error = _root->Render();
            }
        }
    }
    if (error != SFERR_NO_ERROR) {
        // 配信エンジンや描画エンジンの起動時にメモリ不足などの致命的エラーが発生した場合

        if (HandleError(event, error)) {

            result = true;
        }
    }
    if ((event.GetType() == SFEVT_APP_SUSPEND) & IsRendererIntermissive()) {
        // サスペンド時に描画エンジン内部のデバイス画面保存用ビットマップを解放する場合

        // 描画エンジンを終了する
        _renderer.Terminate();
    }
    return result; // イベントを処理したときは true を返し, そうでないときは false を返す

}// SFYApplication::HandleEvent //

参照: SFYApplication::HandleEvent | SFCApplication::HandleError | SFYResponder::Distribute | SFYResponder::Render | SFCApplication::IsRenderable | SFYApplication::IsRendererIntermissive | SFZRoot | ルート | トレーサ | 配信エンジン | 描画エンジン | 描画ハンドラ | イベントループ

9.4.2. ライフサイクル

アプリの起動から終了までの間には、 開始、終了、中断(サスペンド)、再開(レジューム)、キー操作など、 様々なイベントが BREW 環境からアプリへ送信されます。

アプリでは、 これらのイベントを受信しハンドラを呼び出すというイベントドリブンな処理が繰り返されます。

以下は、アプリの典型的なライフサイクルの内容です。

図 9.17. ライフサイクル


イベント分岐処理

様々なイベントを適切に分岐する処理は煩雑です。

SophiaFramework UNIVERSE では、 配信エンジンがイベントの分岐処理を自動的に行うので、 プログラムはとてもシンプルです。

9.4.3. アプリの起動

ユーザーがアプリを起動すると、 最初に SFYApplication クラスを継承するアプリケーションクラスのインスタンスが作成されます。

SFYApplication::SFYApplication コンストラクタは、 配信エンジン描画エンジンを初期化し、 それらが関連付けられたルートを作成します。

その後、 アプリは BREW 環境から SFEVT_APP_START イベントを受信し、 アプリの初期化を行います。

[Note] アプリの初期化処理

レスポンダの作成などアプリの初期化処理は、 アプリケーションクラスのコンストラクタ内の処理、または SFEVT_APP_START イベントへの応答として実装します。

図 9.18. アプリの起動


アプリの起動

※ 起動時のシーケンスと関係が薄い描画エンジンとルートは図から省略されています。

9.4.4. アプリの終了

終了メニューや電源キーによってアプリを終了すると、 アプリは 最初に BREW 環境から SFEVT_APP_NO_CLOSE イベントを受信します。

このイベントには false を返します (true を返した場合、アプリは終了しません)。

そして、アプリは BREW 環境から SFEVT_APP_STOP イベントを受信し、 終了処理を行います。

SFEVT_APP_STOP イベントの処理が正常に終了した場合、 SFYApplication::~SFYApplication デストラクタは、 配信エンジン、描画エンジン、ルート、アプリケーションクラスやウィンドウなどレスポンダのインスタンスを解放します。

[Note] アプリの終了処理

レスポンダの破棄などのアプリの終了処理は、 アプリケーションクラスのデストラクタ内の処理、または SFEVT_APP_STOP イベントへの応答として実装します。

図 9.19. アプリの終了


アプリの終了

上の図では、 ユーザーがウィンドウ(win1)上の終了ボタンを押下した後、 SFCApplication::Terminate 関数が実行されアプリが終了することを想定しています。

※ 終了時のシーケンスと関係の薄い描画エンジンとルートは図から省略されています。

9.4.5. 標準トレーサ

トレーサは、 SFYResponder::Distribute 関数を使用してレスポンダにイベントを配信する規則を管理しています。

標準トレーサとは、 配信エンジンSFYApplication クラスが内部で保持するルートに設定された SFYDistributer インスタンス)のトレーサのことです。 下記の表にあるように、一般のアプリ開発に必要十分なデフォルトの配信規則が予め登録されています。

トレーサの登録内容を変更しない場合、 イベントの配信は標準トレーサを利用して行われます。 ルートは標準トレーサの設定を継承し、 子レスポンダは親レスポンダのトレーサの設定を継承します。

[Note] 標準トレーサのカスタマイズについて

ほとんどのアプリ開発において、 標準トレーサの設定内容をカスタマイズする必要はありません。

トレーサに登録する配信規則には、以下の 3 つの要素があります。

  1. 配信条件
  2. 処理順序
  3. 重複条件

■配信条件

配信条件は、 イベントを配信するレスポンダの状態を表します。

以下のいずれかを指定します。

  1. フォーカス[SFYTracer::STATE_FOCUS]: フォーカス状態が ON である(フォーカスされている)レスポンダに配信します。
  2. すべて[SFYTracer::STATE_ALL]: 有効状態が ON であるレスポンダに配信します。
  3. なし[SFYTracer::STATE_NONE]: 配信先のレスポンダ内だけでイベントを処理します(子レスポンダにイベントは配信されません)。 配信エンジン(SFYApplication クラスが内部で保持するルートに設定された SFYDistributer インスタンス) のトレーサ(標準トレーサ)の配信規則である場合、イベントはルートを含めどのレスポンダにも配信されません。

キーイベントでは、配信条件が『フォーカス[SFYTracer::STATE_FOCUS]』に設定されているので、 フォーカスされているレスポンダに配信されます。

アプリの開始、終了、サスペンド、レジュームのイベントでは、 配信条件が『すべて[SFYTracer::STATE_ALL]』に設定されているので、 有効状態が ON である、すべてのレスポンダに配信されます。

[Caution] 標準トレーサに『配信条件: なし[SFYTracer::STATE_NONE]』で配信規則が登録されている BREW イベントを受信する方法

標準トレーサに『配信条件: なし[SFYTracer::STATE_NONE]』 で配信規則が登録されているBREW イベントは、 SFYApplication が内部で保持するルートを含めレスポンダには配信されません。

これは標準トレーサの配信規則が SFYDistributer インスタンスに登録されているからです。

このイベントは、 直接 SFYDistributer インスタンスにハンドラを登録して受信します。 具体的には以下の方法で行います。

  1. SFYApplication::GetDistributer 関数を使用して SFYDistributer のインスタンスを取得します。
  2. SFYDistributer::RegisterHandler 関数を使用してハンドラを登録します。
  3. ハンドラはどのクラスに定義してもかまいませんが、 この場合、invoker 引数には null が渡される点に注意してください。

図 9.20. SFEVT_KEY イベント: 配信条件


SFEVT_KEY イベント: 配信条件

SFEVT_KEY イベントは、フォーカスされているレスポンダに配信されます。

図 9.21. SFEVT_APP_RESUME イベントの配信


SFEVT_APP_RESUME イベントの配信

SFEVT_APP_RESUME イベントは、すべてのレスポンダに配信されます。

■処理順序

処理順序は、 イベントを配信する順序とハンドラを呼び出す優先順位を表します。

以下のいずれかを指定します。

  1. 背面から前面[SFYTracer::ORDER_BACKWARD]: イベントを背面から前面の順にレスポンダに配信します。 同じレスポンダに複数のハンドラがあった場合は、登録の順に呼び出します。
  2. 前面から背面[SFYTracer::ORDER_FORWARD]: イベントを前面から背面の順にレスポンダに配信します。 同じレスポンダに複数のハンドラがあった場合は、登録の逆順に呼び出します。

■重複条件

重複条件は、 ハンドラがイベントを重複して処理するかどうかを表します。

以下のいずれかを指定します。

  1. 有り[true]: イベントがハンドラによって処理されても、無くなるまで継続して次のハンドラを呼び出します。
  2. 無し[false]: イベントがハンドラによって処理されると、そこで処理を終えます。

SFEVT_KEY イベントは、 配信条件が『フォーカス[SFYTracer::STATE_FOCUS]』、 処理順序が『前面から背面[SFYTracer::ORDER_FORWARD]』に設定されているので、 前面から背面の順にフォーカスされているレスポンダに配信されます。 同じレスポンダに複数個のハンドラが登録されている場合は、ハンドラは登録の逆順に呼び出されます。 重複条件は『なし』に設定されているので、 イベントがハンドラによって処理されると、そこでイベント処理は終了します。

SFEVT_APP_RESUME イベントは、 配信条件が『すべて[SFYTracer::STATE_ALL]』、 処理順序が『背面から前面[SFYTracer::ORDER_BACKWARD]』に設定されているので、 背面から前面の順に状態に関係なく、 有効状態が ON である、すべてのレスポンダに配信されます。 同じレスポンダに複数個のハンドラが登録されている場合は、登録の順に呼び出されます。 重複条件は『あり』に設定されているので、 ハンドラのイベント処理に関係なく、無くなるまで継続して次のハンドラを呼び出します。

図 9.22. SFEVT_KEY イベント: 処理順序と重複条件


SFEVT_KEY イベント: 処理順序と重複条件

SFEVT_KEY イベントは、前面から背面の順に処理されます。

  1. SFEVT_KEY イベントは、 最初に最子階層のフォーカスされている SFYControl に配信されます。 イベントが処理された場合、そこで処理を終えます。
  2. SFYControl でイベントが処理されなかった場合、 その次にフォーカスされている SFZWindow に配信されます。 イベントが処理された場合、そこで処理を終えます。
  3. SFZWindow でもイベントが処理されなかった場合は、 その次にフォーカスされている SFYApplication が内部で保持するルートに配信されます。 イベントが処理された場合、そこで処理を終えます。
  4. 同じレスポンダに SFEVT_KEY イベントのハンドラが複数個登録されていた場合、 ハンドラは登録の逆順に呼び出されます。

■標準トレーサの設定

標準トレーサの設定は以下の表のとおりです。

表 9.3. 標準トレーサ

イベントタイプ 配信条件 処理順序 重複条件
SFEVT_APP_START すべて 背面から前面 あり
SFEVT_APP_STOP すべて 前面から背面 あり
SFEVT_APP_RESUME すべて 背面から前面 あり
SFEVT_APP_SUSPEND すべて 前面から背面 あり
SFEVT_APP_CONFIG から SFEVT_APP_START_WINDOW なし 前面から背面 なし
SFEVT_KEY から SFEVT_KEY_HOOK_RELEASE フォーカス 前面から背面 なし
SFEVT_COMMAND から SFEVT_CTL_TEXT_MODECHANGED フォーカス 前面から背面 なし
SFEVT_DIALOG_INIT から SFEVT_COPYRIGHT_END フォーカス 前面から背面 なし
SFEVT_ALARM から SFEVT_NOTIFY_FAILURE なし 前面から背面 なし
SFEVT_FLIP から SFEVT_SCR_ROTATE なし 前面から背面 なし
SFEVT_CB_CUT から SFEVT_CB_PASTE なし 前面から背面 なし
[Warning] 警告

トレーサを利用して配信するイベントは、BREW イベントまたはユーザーイベントです。 レスポンダイベントはトレーサを利用して配信してはいけません。

ユーザーイベントを配信するには、トレーサに配信規則を登録する必要があります。

[Caution] 標準トレーサに『配信条件: なし[SFYTracer::STATE_NONE]』で配信規則が登録されている BREW イベントを取得する方法

標準トレーサに『配信条件: なし[SFYTracer::STATE_NONE]』 で配信規則が登録されているBREW イベントは、 SFYApplication が内部に保持する SFYDistributer インスタンスだけに配信されます (ルートを含めレスポンダには配信されません)。

この場合、 ハンドラを SFYDistributer インスタンスに登録して BREW イベントを処理する必要があります。

具体的には、以下のようにして行います。

  1. SFYApplication::GetDistributer 関数により、 SFYApplication クラスが内部に保持している SFYDistributer インスタンスを取得します。
  2. SFYDistributer::RegisterHandler 関数により、 ハンドラを SFYDistributer インスタンスに登録します。
  3. ハンドラはどのクラスに定義してもかまいませんが、 この場合、invoker 引数には null が渡される点に注意してください。

例 9.25. トレーサへの配信規則の登録

USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {

        // ...(省略)...

        static_throw(RegisterTracer(
            SFXEventRange(SFEVT_KEY, SFEVT_KEY, AVK_SOFT1, AVK_SOFT4),
            SFYTracer::ORDER_FORWARD, SFYTracer::STATE_ALL, false
        ));

        // ...(省略)...
    }
}
[Tip] Tip

配信規則は SFYResponder::RegisterTracer / SFYDistributer::RegisterTracer 関数を使用してトレーサに登録します。

参照: トレーサ | 配信エンジン | イベント | ハンドラ

9.4.6. ハンドラ

ハンドラとは、 レスポンダが受信したイベントを処理する関数のことです。

ハンドラは、 SFYResponder::RegisterHandler / SFYDistributer::RegisterHandler 関数を使用してレスポンダやルートに設定された SFYDistributer インスタンスにイベントと関連付けて登録します。

ハンドラは、同じイベントに対して複数個登録できます。

例えば、 複数のハンドラをセレクトキーの SFEVT_KEY イベントに関連付けてウィンドウに登録したとします。

SFEVT_KEY イベントに関する 標準トレーサに登録された配信規則の処理順序は 「前面から背面」なので、 ウィンドウが SFEVT_KEY イベントを受信すると、 最後に登録したハンドラが最初に呼び出されます。

このハンドラが true を返すと、イベント処理はここで終了します。 false を返した場合は、 最後から 2 番目に登録したハンドラが呼び出されます。

このウィンドウのイベント処理は、何れかのハンドラが true を返すか、 ウィンドウに登録されたハンドラが無くなるまで続けられます。

どのハンドラにも処理されなかった場合は、 ウィンドウよりももう 1 つ背面にあるフォーカスされているレスポンダで同様の処理が行われます。

[Note] ハンドラの配置関係

ハンドラは、背面から前面へ登録順に配置されます (最初に登録されたハンドラは最背面、最後に登録されたハンドラは最前面に配置されます)。

例 9.26. ハンドラの登録

USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {

        // ...(省略)...

        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
            XANDLER_INTERNAL(OnKey)
        ));

        // ...(省略)...
    }
}

例 9.27. ハンドラの実装

XANDLER_IMPLEMENT_BOOLEVENT(USRResponder, OnKey, invoker, event)
{
    switch (event.GetP16()) {

        case AVK_SELECT:

            TRACE("select key was pressed.");
            return true;

        case AVK_CLR:

            TRACE("clear key was pressed.");
            return true;

        default:

            TRACE("unknown key was pressed.");
            return true;
    }

    return false;
}
[Important] 重要

ハンドラは、イベントを処理したときは true、 しなかったときは false を返すように実装します。

イベントループ内の処理で 1 つ以上のハンドラが true を返した場合、 レスポンダシステムは SFYResponder::Render 関数を呼び出して描画エンジンを起動し、 再描画を行います。

参照: SFYApplication::HandleEvent 関数の内部実装

参照: ハンドラ | イベント | SFYResponder::RegisterHandler | SFYDistributer::RegisterHandler | SFYApplication::HandleEvent