前のページ次のページ上に戻るホーム BREW C++ ライブラリ & GUI フレームワーク & XML ミドルウェア : SophiaFramework UNIVERSE 5.0

9.3. レスポンダシステムの機能

レスポンダシステムには、 レスポンダツリーレスポンダ空間イベント処理描画処理などレスポンダを利用する上で重要な機能があります。

この節では、これらのレスポンダシステムの機能について説明します。

関連情報 : レスポンダシステムを構成する要素

9.3.1. レスポンダツリー

レスポンダツリーとは、レスポンダを管理するための木構造のことです。 レスポンダツリーの頂点にあるレスポンダは、 ルートレスポンダと呼ばれます。

ルートレスポンダに配信エンジン描画エンジンを関連付ければ、 レスポンダツリー上のレスポンダはイベントを受信したり、画面に描画することが可能になります。

[Note] ルート

SFYApplication クラスは、 デフォルトのルートレスポンダとして描画エンジンと配信エンジンが関連付けられたルート(SFZRoot クラス)を保持しています。

[Caution] 注意

レスポンダは、作成してからレスポンダツリーに接続されるまでの間は親レスポンダが存在しないので、 一時的にルートレスポンダになっています。

図 9.14. レスポンダツリーの構造


レスポンダツリーの構造

SFZWindow(参照:右側の不完全なレスポンダツリー)は、 SFZRoot(参照:左側の完全なレスポンダツリー)を親レスポンダにするまでの間は、一時的にルートレスポンダになっています。

例 9.21. レスポンダツリーの作成

SFCError USRApplication::MakeTree(Void)
{
    SFZRootSmp root;
    SFZDialogSmp dialog;
    SFZTextButtonControlSmp button;
    SFZWindowSmp window;
    SFZTextLabelControlSmp label;
    SFCError error(SFERR_NO_ERROR);

    if ((root = SFZRoot::NewInstance(&error)) != null) { // 実際のアプレット開発では、ルートの作成は省略される
        if ((dialog = SFZDialog::NewInstance(&error)) != null) {
            if ((error = dialog->SetParent(root)) == SFERR_NO_ERROR) {
                if ((button = SFZTextButtonControl::NewInstance(&error)) != null) {
                    if ((error = button->SetParent(dialog)) == SFERR_NO_ERROR) {
                        ...
                    }
                }
            }
        }
        if (error == SFERR_NO_ERROR) {
            if ((window = SFZWindow::NewInstance(&error)) != null) {
                if ((error = window->SetParent(root)) == SFERR_NO_ERROR) {
                    if ((label = SFZTextLabelControl::NewInstance(&error)) != null) {
                        if ((error = label->SetParent(window)) == SFERR_NO_ERROR) {
                            ...
                        }
                    }
                }
            }
        }
    }
    return error;
}
[Caution] 注意

実際のアプレット開発では、SFYApplication クラスがルート(SFZRoot)を保持しているため、ルート(SFZRoot)の作成は省略されます。

レスポンダツリーは下記の順序関係で画面に表示されます。

  • 順序関係 1 : 子レスポンダは親レスポンダよりも前面にあります。
  • 順序関係 2 : 姉レスポンダは妹レスポンダよりも前面にあります。
  • 順序関係 3 : 姉レスポンダは妹レスポンダの子レスポンダよりも前面にあります。

[Tip] ToFront() 関数

ボタンやラベルなどを子レスポンダとして持つウィンドウの SFYResponder::ToFront 関数を呼び出すと、 ウィンドウだけでなくボタンやラベルなど子レスポンダも一緒に最前面に移動します。

関連情報 : ルートレスポンダ | 描画エンジン | 配信エンジン | ルート | SFZRoot | SFYDistributer | SFYRenderer | SFYResponder::SetDistributer | SFYResponder::SetRenderer

9.3.2. レスポンダ空間

レスポンダ空間とは、 描画エンジンに関連付けられたレスポンダツリーを描画する、 画面の矩形領域のことです。

レスポンダ空間は SFYRenderer::Initialize 関数を利用して描画エンジンを初期化するときに設定します。

デフォルトの設定では、画面の全領域がレスポンダ空間として設定されています。

画面の部分的な領域をレスポンダ空間として設定できますが、 レスポンダ空間の中にしかレスポンダツリーは描画されません。

[Note] ルートの実領域

SFYApplication クラスは、 デフォルトで描画エンジンと配信エンジンに関連付けらたルートレスポンダとして SFZRoot クラス(ルート)を保持しています。

デフォルト設定ではルートの実領域はレスポンダ空間と一致させますが、レスポンダ空間より大きくしたり、小さくできます。

レスポンダ空間よりも大きく実領域を設定した場合、 レスポンダ空間の矩形領域の外にはレスポンダツリーは描画されません。

[Note] レスポンダ空間の変更

ルートの実領域は動的に任意に再設定可能ですが、レスポンダ空間は描画エンジンの初期設定時の内容を変更できません。

[Note] 独自レスポンダ空間

独自に描画エンジンや配信エンジンを生成して新たなレスポンダツリーを構成するときは、レスポンダ空間を設定する必要があります。

関連情報 : SFYRenderer::Initialize | SFYRenderer | SFZRoot | レスポンダツリー | 描画エンジン | 配信エンジン | ルート | 実領域

9.3.3. イベント処理

レスポンダシステムのイベント処理の概要は以下のとおりです。

最初に、ユーザー操作や外的要因によって発生した割り込みがBREW 定義イベントとして、 BREW 環境からアプレットに送信されます。

BREW 定義イベントはトレーサを利用してレスポンダに配信され、 ハンドラが呼び出されます。

ハンドラ内の処理で、 レスポンダ定義イベントまたはユーザー定義イベントが発生すると、 そのハンドラが呼び出されます。

9.3.3.1. ライフサイクル

BREW アプレットの起動から終了までの間には、 開始、終了、サスペンド、レジューム、キー操作などさまざまなイベントが BREW 環境から BREW アプレットに送信されてきます。

BREW アプレットでは、 これらのイベントを受信しハンドラを駆動するというイベントドリブンな分岐処理を繰り返します。

以下に BREW アプレットの典型的なライフサイクルを示します。

図 9.15. ライフ サイクル


イベント分岐処理

BREW アプレットが受信するさまざまなイベントを適切に分岐処理するのは煩雑な作業です。

レスポンダシステムでは配信エンジンがイベントの分岐処理を自動的に行ってくれるため、 プログラムの構造はとてもシンプルになります。

9.3.3.2. アプレットの起動

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

アプリケーションクラスのインスタンスは配信エンジン描画エンジンルート(SFZRoot)を作成します。

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

ウィンドウの作成やその他の初期化処理は、 アプリケーションクラスのコンストラクタ内や SFEVT_APP_START イベントへの応答として実装します。

図 9.16. アプレットの起動


アプレットの起動

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

9.3.3.3. アプレットの終了

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

このイベントに true を返すとアプレットは終了しませんので、このイベントには false を返します。

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

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

アプレットの起動と対称的に、 ウィンドウの破棄やその他の終了処理は、 アプリケーションクラスのデストラクタ内や SFEVT_APP_STOP イベントへの応答として実装します。

図 9.17. アプレットの終了


アプレットの終了

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

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

9.3.3.4. 標準トレーサ

トレーサは、 レスポンダにイベントを配信する規則を管理しています。

レスポンダシステムでは、 デフォルトの配信規則が標準トレーサとして配信エンジンに登録されています。

明示的にトレーサが変更されない場合、イベントの配信は標準トレーサを利用して行われます(子レスポンダは、親レスポンダでのトレーサの設定を継承します)。

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

標準トレーサの設定を変更することも可能ですが、 一般的なアプレット開発では標準トレーサの設定内容で十分です。

トレーサに登録する配信規則には、 配信条件処理順序重複条件の3つの要素があります。

■配信条件

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

通常は、以下のいずれかを指定します。

  1. フォーカス[SFYTracer::STATE_FOCUS] : フォーカスされているレスポンダに配信します。
  2. すべて[SFYTracer::STATE_ALL] : すべてのレスポンダに配信します。
  3. なし[SFYTracer::STATE_NONE] : 配信先のレスポンダ内だけでイベントを処理します(子レスポンダにイベントは配信されません)。

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

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

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


SFEVT_KEY イベントの配信

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

図 9.19. SFEVT_APP_RESUME イベントの配信


SFEVT_APP_RESUME イベントの配信

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

■処理順序

処理順序では、 ハンドラが呼び出される優先順位を指定します。

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

  1. 背面から前面[SFYTracer::ORDER_BACKWARD] : 背面に配置されたレスポンダのハンドラから優先的に呼び出されます。 同じレスポンダに複数のハンドラがあった場合は、登録の順で呼び出されます。
  2. 前面から背面[SFYTracer::ORDER_FORWARD] : 前面に配置されたレスポンダのハンドラから優先的に呼び出されます。 同じレスポンダに複数のハンドラがあった場合は、登録の逆順で呼び出されます。

■重複条件

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

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

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

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

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

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


SFEVT_KEY イベントが処理される優先順位

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

  1. SFEVT_KEY イベントは、最初に最子階層のフォーカスされた SFYControl で処理されます。イベントが処理された場合、そこで処理を終えます。
  2. SFYControl で処理されなかった場合、その次にフォーカスされた SFZWindow で処理されます。イベントが処理された場合、そこで処理を終えます。
  3. SFZWindow でも処理されなかった場合、その次にフォーカスされたSFYApplication(SFZRoot) で処理されます。イベントが処理された場合、そこで処理を終えます。
  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 定義イベントまたはユーザー定義イベントです。 レスポンダ定義イベントはトレーサを利用して配信してはいけません。

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

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

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

配信規則は RegisterTracer() 関数を利用してトレーサに登録します。

関連情報 1 : 配信型 | コールバック型 | イベント一覧 | BREW 定義イベント | レスポンダ定義イベント | ハンドラ一覧 | ハンドラ | SFYResponder::RegisterHandler | SFYResponder::UnregisterHandler | SFYResponder::ClearHandler | SFYResponder::Distribute | SFYResponder::InvokeBackward | SFYApplication::InvokeBackward | SFYApplication::RegisterHandler | SFYApplication::UnregisterHandler | SFYApplication::ClearHandler | SFYApplication::Distribute | SFYResponder::InvokeForward | SFYApplication::InvokeForward | SFYHandler | SFXEvent | SFXEventRange

関連情報 2 : 配信エンジン | トレーサ | SFYResponder::RegisterTracer | SFYResponder::UnregisterTracer | SFYResponder::ClearTracer | SFYApplication::RegisterTracer | SFYApplication::UnregisterTracer | SFYApplication::ClearTracer | SFYDistributer | SFYTracer

9.3.3.5. ハンドラ

ハンドラとは、 レスポンダに配信されてきたイベントを実際に処理する関数のことです。

ハンドラは1つのレスポンダに複数個登録することができ、 また、同じイベントに対して複数個のハンドラを登録ことも可能です。

たとえば、 ウィンドウ上でセレクトキーの SFEVT_KEY イベントに対して複数のハンドラを登録した場合、 標準トレーサに設定された規則の処理順序は「前面から背面」なので、 最後に登録したハンドラが最初に呼び出されます。

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

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

ハンドラの配置は、最初に登録したものが背面、最後に登録したものが前面となります。

例 9.23. ハンドラの登録

USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {
        ...
        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
            XANDLER_INTERNAL(OnKey)
        ));
        ...
    }
}
[Tip] Tip
ハンドラは SFYResponder::RegisterHandler 関数を利用して登録します。

例 9.24. ハンドラの実装

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 を返した場合、 レスポンダシステムは再描画が必要と判断し描画エンジンを起動します。

[Tip] コールバック型イベント

InvokeForward() または InvokeBackward() 関数を利用して送信するイベントのことをコールバック型イベントと定義します。 レスポンダ定義イベントはコールバック型イベントです。

関連情報 1 : ハンドラ | イベント一覧 | BREW 定義イベント | レスポンダ定義イベント | ハンドラ一覧 | トレーサ | SFYResponder::RegisterHandler | SFYResponder::UnregisterHandler | SFYResponder::ClearHandler | SFYApplication::RegisterHandler | SFYApplication::UnregisterHandler | SFYApplication::ClearHandler | SFYHandler | SFXEvent | SFXEventRange

関連情報 2 : コールバック型 | SFYResponder::InvokeForward | SFYResponder::InvokeBackward | SFYApplication::InvokeForward | SFYApplication::InvokeBackward

関連情報 3 : 配信型 | SFYResponder::Distribute | SFYResponder::RegisterTracer | SFYResponder::UnregisterTracer | SFYResponder::ClearTracer | SFYApplication::RegisterTracer | SFYApplication::UnregisterTracer | SFYApplication::ClearTracer | SFYTracer

9.3.4. 描画処理

配信エンジンがイベント処理を終え、 1つ以上のハンドラが true を返した場合、 レスポンダシステムは再描画が必要と判断し描画エンジンを起動します。

描画エンジンは各レスポンダの状態や配置関係を自動的に計算し、 再描画が必要なレスポンダに対して描画イベントを発生させます。

イベントループ毎に必要最少限の描画ハンドラしか呼び出されないため、 大規模なユーザーインターフェースを構築してもパフォーマンスが劣化することはありません。

[Note] 見えないレスポンダの描画処理

イベントループ内の処理によってテキスト、色などレスポンダの内容が変化しても、 不可視状態であったり、他のレスポンダに隠れて変化した内容が見えない場合は、 描画ハンドラは呼び出されません。

9.3.4.1. 描画のタイミング

描画エンジンは、 イベントループの最後に自動的に起動されると、 再描画領域が登録されているかどうかを検査し、 本当に再描画が必要な領域を含むレスポンダだけに描画イベントを発生させます。

描画イベントが発生したレスポンダでは、描画ハンドラが呼び出されて再描画が行われます。

[Note] 高速な GUI 処理

BREW 標準では、 キーイベントを受信すると、 フォーカス移動させる必要があればすぐにフォーカスの移動と描画の処理を行います。

レスポンダシステムでは、 キーイベントを受信したタイミングではフォーカス移動の処理と再描画領域の登録だけを行います。 実際の描画は、イベントループの最後にレスポンダツリー全体についてまとめて一度だけ行います。

このとき、 あるレスポンダが他のレスポンダの背面にあって画面に表示されない場合や、 画面の内容に変化がない場合、描画ハンドラが呼び出されません。

BREW 標準では再描画される GUI コンポーネントのいくつかは、 レスポンダシステムでは再描画されないのでそれだけレスポンスは速くなります。

図 9.21. イベントが処理された場合


イベントが処理された場合

イベントループでイベントが処理された場合、描画エンジンは自動的に起動されます。

イベントループの処理ですべてのハンドラが false を返し描画エンジンが起動されない場合や 再描画すべき領域が存在しない場合、描画は行われません。

図 9.22. イベントが処理されなかった場合


イベントが処理されなかった場合

イベントループでイベントが処理されなかった場合、描画エンジンは起動されません。

9.3.4.2. 描画ハンドラ

SFYResponder::Render 関数を呼び出すと起動される描画エンジンにより、 実際に再描画が必要な領域を含むレスポンダだけで描画イベントが発生します。

[Caution] 注意
SFYResponder::Render 関数はイベントループの最後で自動的に呼び出されますが、 コールバックなどイベントループ外での処理で画面を再描画するには明示的にこの関数を呼び出す必要があります。

描画イベントが発生すると、 レスポンダに関する描画処理が記述された描画ハンドラと呼ぶ描画専用のハンドラが起動されます。

描画イベント発生時にデフォルトで呼び出される SFYWidget::HandleRenderRequest 仮想関数も描画ハンドラの一種であり、 レスポンダが自分自身を描画する場合はこの仮想関数をオーバーライドして描画処理を記述します。

SFYWidget::HandleRenderRequest 仮想関数をオーバーライドし、かつ複数の描画ハンドラを登録することも可能です。 このとき、最初にオーバーライドされた仮想関数が呼び出され、その後、レスポンダへの登録順で描画ハンドラが呼び出されて実行されます。

例 9.25. 描画ハンドラの登録

// 描画ハンドラの登録
USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {

        ...

        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST),
            XANDLER_INTERNAL(OnRenderRequest)
        ));

        ...

    }
}
[Tip] Tip

描画ハンドラもハンドラの一種であり、 SFYResponder::RegisterHandler 関数を利用して登録します。

例 9.26. 描画ハンドラの実装

// 描画ハンドラの実装
XANDLER_IMPLEMENT_VOIDRENDER(USRResponder, OnRenderRequest, invoker, reason, graphics)
{
    graphics->FillRectangle(GetLocalBound(), SFXRGBColor(0xFF, 0xFF, 0xFF, 0x00));
    return;
}
[Tip] Tip

描画ハンドラでは、ハンドラの引数として SFXGraphics クラスのインスタンスを取得して、 レスポンダのローカル領域に対して描画処理を行います。

9.3.4.3. 再描画領域の登録

レスポンダシステムでは、 描画エンジンを起動するまでに再描画が必要になった場合は、 SFYResponder::Invalidate 関数を利用して再描画領域の登録だけを行います。

実際の描画処理は、 イベントループの最後に起動される描画ハンドラに分けて記述します。

たとえば、下記のコードにある USRResponder::SetText 関数を利用してラベルのテキストを変更すると、 ラベルの内容が変更されたのでラベルコントロールを再描画しなければいけません。

このタイミングでは、 SFYResponder::Invalidate 関数を利用して再描画領域を描画エンジンに登録するだけです。 ラベルの描画処理は描画ハンドラ内に分けて記述します。

イベントループの最後に、 描画エンジンが起動され、それまでに登録された再描画領域を検査し、 実際に画面に表示される再描画領域の描画ハンドラだけが呼び出されます。

[Note] 注意

不可視状態であったり、他のレスポンダの領域に隠れて画面に表示されないレスポンダの描画ハンドラが呼び出されることはありません。

例 9.27. 再描画領域の登録

// 描画ハンドラの登録
USRResponder::USRResponder(Void) static_throws
{
    if (static_try()) {

        ...

        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST),
            XANDLER_INTERNAL(OnRenderRequest)
        ));

        ...

    }
}

Void USRResponder::SetText(SFXAnsiStringConstRef param)
{
    SFCError error(SFERR_NO_ERROR);

    if (!param.equals(_text)) {
        if ((error = _text.Set(param)) == SFERR_NO_ERROR) {
            Invalidate(); // このタイミングではラベルを描画せずに再描画領域の登録だけを行う
        }
    }
    return error;
}

// 描画ハンドラの実装(実際の再描画は描画ハンドラ内に記述する)
XANDLER_IMPLEMENT_VOIDRENDER(USRResponder, OnRenderRequest, invoker, reason, graphics)
{
    SFXRectangle local;

    // ラベルの描画処理は描画ハンドラ内に記述する
    local.Set(GetLocalBound());
    graphics->FillRectangle(local, SFXRGBColor(0xFF, 0xFF, 0xFF, 0x00));
    graphics->DrawSingleText(_text, local, SFXRGBColor(0x00, 0x00, 0x00, 0x00));
    return;
}
[Important] 重要

再描画が必要になる USRResponder::SetText() 関数内では再描画領域を登録するだけです。 実際の描画処理はイベントループの最後のタイミングで起動される描画ハンドラ内に記述します。

[Tip] 高速化のためのヒント

SFYResponder::Invalidate 関数には矩形の引数を取るオーバーロード関数が存在します。

再描画領域をレスポンダの部分的な矩形領域に限定すると、 前面に配置されたレスポンダ領域の和集合に含まれる可能性が高まり、 その場合描画処理のパフォーマンスは向上します。

9.3.4.4. 明示的な再描画

ネットワークやタイマー処理などイベントループ外のコールバック呼び出しでは、 描画エンジンは自動的に起動されません。

コールバック処理で画面を再描画するには、 再描画領域を登録してから明示的に SFYResponder::Render 関数を利用して描画エンジンを起動する必要があります。

再描画のタイミングでレスポンダが破棄されている場合は、 親レスポンダの SFYResponder::Render 関数、 またはアプリケーションクラスの SFYApplication::Render 関数を呼び出します。

図 9.23. コールバック呼び出し時の描画処理1: Render() 関数を呼び出さない場合


Render() 関数を呼び出さない場合

コールバックはイベントループ外の処理なので、処理を終えても描画エンジンは自動的に起動されません。 そのため、SFYResponder::Invalidate 関数を利用して再描画領域を登録しても画面には何も描画されません。

図 9.24. コールバック呼び出し時の描画処理2: Render() 関数を呼び出す場合


Render() 関数を呼び出す場合
  1. SFYResponder::Invalidate 関数を利用して再描画領域を登録します。
  2. 明示的に、再描画が必要なレスポンダの SFYResponder::Render 関数を呼び出します。
  3. 描画エンジンが起動し、描画ハンドラが呼び出されます。
  4. 実際に画面が再描画されます。

例 9.28. コールバック内での再描画

SFCError USRResponder::OpenConnection(Void)
{
    SFCError error(SFERR_NO_ERROR);

    _label->SetText("); 

    ...

    error = _http.Connect(XALLBACK_INTERNAL(OnConnect));

    ...

    return error;
}

// http 接続完了コールバックの実装
XALLBACK_IMPLEMENT_SFXHTTPCONNECTION(USRResponder, OnConnect, error)
{
    if (error == SFERR_NO_ERROR) {

        ...

    }
    _label->SetText("OnConnect was called."); // Invalidate() 関数は SetText() 関数の内部で呼び出される
    Render(); // 明示的な再描画(明示的な描画エンジン起動)
    return;
}
[Note] 明示的な再描画を高速に行う方法

効率良く描画を行うには、1つのコールバック呼び出しにつき1回だけ描画エンジンを起動するのが理想です。

コールバック関数内の最後で、 レスポンダツリー内で再描画の可能性のあるすべてのレスポンダを包含するレスポンダの SFYResponder::Render 関数を呼び出すとよいでしょう。

[Caution] Render(true) の実行について

SFYResponder::Render 関数の引数に true を指定すると、 画面に表示されているレスポンダを強制的に再描画します。必要でないかぎり、Render(true) の実行は避けることを推奨します。

Render(true) は、レスポンダツリー全体を構築または再構築して画面に再描画する必要があるときに実行します。 アプレット開始時、レジューム時、またはテキスト入力コントロールからアプレットに戻ったときに、アプリケーションクラスの内部で自動的に実行されます。

※不可視状態であったり、他のレスポンダに隠れて画面に表示されないレスポンダの描画ハンドラは呼び出されません。