ホーム > 製品情報 > SophiaFramework UNIVERSE > SophiaFramework UNIVERSE GUI フレームワーク for BREW

SophiaFramework UNIVERSE GUI フレームワーク for BREW

目次

※ SophiaFramework UNIVERSE 5.1 の SFY レスポンダシステム(新 GUI フレームワーク)については下記 URL のサイトをご覧ください。

  1. GUI フレームワークとは
  2. GUI フレームワークの使い方
  3. GUI フレームワークの構造
  4. イベント処理
    1. イベントドリブン
    2. アプリの起動
    3. アプリの終了
    4. トレーサ
    5. 標準トレーサ
    6. ハンドラ
  5. 描画処理
    1. 描画のタイミング
    2. 描画ハンドラ
    3. 再描画領域の登録
    4. 明示的な再描画

GUI フレームワークとは

GUI フレームワークとは、ウィンドウボタンチェックボックス などの GUI コンポーネント を画面上に配置するだけで自動的に イベント の分岐や画面の再描画を行う、GUI フレームワークです。

複雑な イベント の分岐処理を記述したり、GUI コンポーネント を独自に描画したり、GUI コンポーネント の状態を管理する必要はありません。

SophiaFramework UNIVERSE の GUI フレームワーク では、レスポンダ(Responder) と呼ばれる GUI コンポーネント が提供されます。

レスポンダ一覧

■ アプリ
クラス名 解説
SFRApplication アプリの基底クラス ※

■ ウィンドウ
クラス名 解説
SFRWindow ウィンドウの基底クラス ※
SFRPlainWindow 枠のない平面的なウィンドウ
SFRFrameWindow 枠のある平面的なウィンドウ
SFRTitleWindow 枠とタイトルバーを伴った立体的なウィンドウ

■ ダイアログ
クラス名 解説
SFRDialog ダイアログの基底クラス ※
SFRPlainDialog 枠のない平面的なダイアログ
SFRFrameDialog 枠のある平面的なダイアログ
SFRTitleDialog 枠とタイトルバーを伴った立体的なダイアログ
SFRMessageDialog 最高1つのボタンを持つメッセージダイアログ
SFRMultiDialog 最高3つのボタンを持つ選択ダイアログ

■ メニュー
クラス名 解説
SFRMenu メニューの基底クラス ※
SFRTextMenu テキストで構成された項目を表示するメニュー

■ ペイン
クラス名 解説
SFRPane ペインの基底クラス ※
SFRPlainPane 枠のない平面的なペイン
SFRTabPane SFRTabControl 専用のペイン

■ コントロール
クラス名 解説
SFRControl コントロールの基底クラス ※
SFRLabelControl ラベルコントロール
SFRButtonControl ボタンコントロール
SFRCheckboxControl チェックボックスコントロール
SFRRadiobuttonControl ラジオボタンコントロール
SFRComboboxControl コンボボックスコントロール
SFRTabControl タブコントロール
SFREditboxControl テキスト入力コントロール
SFRBrowserControl HTML ブラウザを表示するコントロール

※ レスポンダの基底クラス( 抽象クラス )は、開発者独自の GUI コンポーネントを作成するときに使用します。

GUI フレームワークの使い方

GUI フレームワーク を利用するには、SFRApplication クラスを継承した アプリクラス を1つ用意します。 アプリウィザード を利用してプロジェクトを作成する場合は、「GUI フレームワークを利用する」のオプションを選択することで、雛型となる アプリクラス が自動生成されます。

アプリクラス は、通常 HelloWorld や MyApp などのアプリ本体と同じ名前のクラスとなります。

例:ウィンドウの生成
SFRWindowPtr window;

window = ::new SFRTitleWindow(SFRApplication::GetInstance(),
                              SFXRectangle(0, 0, 240, 320), 
                              "my window"); 
例:ボタンの配置
SFRButtonControlPtr button;

button = ::new SFRButtonControl(window, 
                                SFXRectangle(10, 10, 128, 24), 
                                "my button"); 
例:デバッグ出力の表示
button->RegisterHandler(SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButton));

HANDLER_IMPLEMENT_VOIDCONTROL(MyApp, OnButton, result, control)
{
    TRACE("my button was pushed.");
    return;
} 

ウィンドウ 上に ボタンチェックボックス などの コントロール を配置し、それらの コントロール が操作されたときに呼び出される関数を登録することによって、複雑な GUI アプリを簡単に作成できます。

GUI フレームワークの構造

SophiaFramework UNIVERSE の GUI フレームワーク は、SFRApplication クラスを継承する アプリクラスウィンドウクラス を管理し、 ウィンドウクラスコントロールクラス を管理するという階層構造になっています。

図:継承関係

SophiaFramework UNIVERSE GUI 継承関係

図:所有関係

SophiaFramework UNIVERSE GUI 所有関係

ダイアログクラスメニュークラスウィンドウクラス と同一階層にあります。 ペインクラスコントロールクラス を管理することも コントロールクラス に管理されることもあります。

この階層構造により、BREW 環境の イベントの適切なディスパッチや、自動的な画面再描画が可能になります。

イベント処理

ユーザの操作によって発生した イベント は BREW 環境からアプリに送信されます。アプリが イベント を受信すると、GUI フレームワークイベント処理エンジンイベント を処理します

イベント処理エンジン は、トレーサ と呼ばれる機能を利用して、可視 / 不可視 などの状態や配置関係に応じ、BREW 環境から渡されたイベントを然るべき レスポンダ に配信します。そして、あらかじめ開発者が登録した ハンドラ と呼ばれる関数を実行します。

イベントドリブン

BREW アプリは、ユーザの操作や外的要因によって発生した割り込みを イベント として伝え、ハンドリングする、イベントドリブン型の構造 をしています。

アプリは イベント の種類に応じて、初期化やキー操作などを分岐処理します。

アプリの起動から終了までの間には、さまざまな イベント が BREW 環境からアプリに送信されます。

シーケンス図:ライフサイクル

シーケンス図 : ライフサイクル

GUI フレームワークイベント処理エンジン は、さまざまな イベント を然るべき処理関数へ自動的にディスパッチしてくれるので、複雑な イベント処理 を簡単に記述できます。

アプリの起動

ユーザがアプリを起動すると、最初に イベント処理エンジン が起動されます。イベント処理エンジンSFRApplication クラスを継承した アプリクラス のインスタンスを1つだけ作成します。

次に BREW 環境からアプリに SFEVT_APP_START イベント が送信され、アプリの初期化処理が行われます。

シーケンス図:アプリの起動

シーケンス図 : アプリの起動

ウィンドウ の作成など初期化処理は、アプリクラスコンストラクタ 内、または SFEVT_APP_START イベント のハンドラ内で行います。

アプリの終了

終了メニュー電源キー の押下によってアプリを終了させる場合、最初に BREW 環境から イベント処理エンジンSFEVT_APP_NO_CLOSE イベント が送信されます。 イベント処理エンジン は、このイベントに対して false を返します。

SFEVT_APP_NO_CLOSE イベント について : アプリを終了しても構わない場合、このイベントに対して false を返します。アプリを終了させないようにするには、このイベントに対して true を返します。

次に BREW 環境から SFEVT_APP_STOP イベント が送信され、アプリの終了処理が行われます。

SFEVT_APP_STOP イベント の処理が正常に終了した場合、 イベント処理エンジン が解放され、アプリクラス のインスタンスも自動的に解放されます。

シーケンス図:アプリの終了

シーケンス図 : アプリの終了

この例ではユーザが ウィンドウ 上の 終了ボタン を押下した時に、SFRApplication::Terminate() 関数が実行されアプリは終了します。

ウィンドウ の破棄など終了処理は、アプリクラスデストラクタ 内、または SFEVT_APP_STOP イベント のハンドラ内で行います。

トレーサ

イベント は、種類毎にどの レスポンダ に配信されるか定義されています。

例えば、キーイベント は、フォーカスが設定されていてユーザの操作対象になっている レスポンダ に配信されます。また、サスペンドイベントレジュームイベント はすべての レスポンダ に配信されます。

図:SFEVT_KEY イベントの流れ

SFEVT_KEY イベントの流れ

図:SFEVT_APP_RESUME イベントの流れ

SFEVT_APP_RESUME イベントの流れ

イベント処理エンジン では、イベントの配信規則を管理するトレーサによってイベントは種類に応じて適切に配信されます。

すべての レスポンダ親レスポンダトレーサ を継承します。 明示的にオーバーライドしない場合、アプリクラス に登録されている 標準トレーサ がイベントの配信規則として使用されます。

トレーサ を登録するには RegisterTracer() 関数を使います。トレーサ に設定できる規則は次の通りです。 レスポンダ の状態に応じてイベント配信規則を設定できます。

トレーサに設定可能な規則 解説
TRACER_NONE 子レスポンダへ配信しない
TRACER_FORWARD 前面から背面の子レスポンダへ配信する
TRACER_BACKWARD 背面から前面の子レスポンダへ配信する
TRACER_FOCUS フォーカスされている子レスポンダへ配信する
TRACER_PROVIDE イベントの重複処理を許す

例:トレーサーの登録
class MyApp : public SFRApplication {
    public:
        explicit MyApp(Void) static_throws;
};

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

        // ソフトキー 1 〜 3 のイベントをすべてのレスポンダに前面から配信する
        //
        // トレーサは子レスポンダへ継承されるので、
        // ウィンドウやコントロールでもこの設定は有効となる

        static_throw(RegisterTracer(SFEVT_KEY,
                                    AVK_SOFT1, 
                                    AVK_SOFT3, 
                                    BEHAVIOR_NONE, 
                                    TRACER_FORWARD));
    }
    return;
}

標準トレーサ

GUI フレームワーク は、 標準トレーサ として以下の表のような トレーサ を保持しています。

配信順序が「」の場合、イベントは子階層のレスポンダにイベントを配信しません。

重複処理が「」の場合、イベントを重複して処理しません。イベントがあるレスポンダで処理された場合、それ以降、そのイベントが他のレスポンダに配信されることはありません。

状態フィルタ の場合、レスポンダ の状態に応じてイベントの配信が可能です。空欄の場合は、状態に関係なく イベント の配信が可能です。

表:標準トレーサ一覧
イベント範囲 配信順序 重複処理 状態フィルタ ※
      V E F T
SFRApplication クラスが保持する BREW イベントのトレーサ
SFEVT_APPLICATION_CLASS_BEGIN
~ SFEVT_APPLICATION_CLASS_END
       
SFEVT_APP_RESUME 前面→背面 許可        
SFEVT_APP_SUSPEND

前面→背面 許可

       
SFEVT_KEY_CLASS_BEGIN
~ SFEVT_KEY_CLASS_END
フォーカス
SFEVT_CONTROL_CLASS_BEGIN
~ SFEVT_CONTROL_CLASS_END
フォーカス
SFEVT_CTL_TAB フォーカス 許可
SFEVT_CTL_TEXT_MODECHANGED フォーカス 許可
SFEVT_DIALOG_CLASS_BEGIN
~ SFEVT_DIALOG_CLASS_END
フォーカス
SFEVT_SHELL_CLASS_BEGIN
~ SFEVT_SHELL_CLASS_END
許可        
SFEVT_DEVICE_CLASS_BEGIN
~ SFEVT_DEVICE_CLASS_END
許可        
SFEVT_CLIPBOARD_CLASS_BEGIN
~ SFEVT_CLIPBOARD_CLASS_END
許可        
SFRApplication クラスが保持する SophiaFramework UNIVERSE イベントのトレーサ
SREVT_RESPONDER_INITIALIZE 許可        
SREVT_RESPONDER_TERMINATE        
SREVT_RESPONDER_TERMINATE,
SRP16_TERMINATE_TRY
前面→背面 許可        
SREVT_RESPONDER_RENDER 許可      
SREVT_RESPONDER_STATUS        
SREVT_APPLICATION        
SFRWindow クラスが保持する SophiaFramework UNIVERSE イベントのトレーサ
SREVT_WINDOW        
SFRDialog クラスが保持する SophiaFramework UNIVERSE イベントのトレーサ
SREVT_DIALOG        
SFRMenu クラスが保持する SophiaFramework UNIVERSE イベントのトレーサ
SREVT_MENU        
SFRPane クラスが保持する SophiaFramework UNIVERSE イベントのトレーサ
SREVT_PANE        
SFRControl クラスが保持する SophiaFramework UNIVERSE イベントのトレーサ
SREVT_CONTROL        

※ 状態フィルタについて
状態フィルタ 解説
V 可視 / 不可視
E 操作可能 / 操作不可能
F フォーカス状態 / 非フォーカス状態
T ターゲット状態 / 非ターゲット状態

ハンドラ

ユーザ操作によって発生した イベント やアプリ間通信の イベント などのさまざまなイベントを実際に処理する関数を ハンドラ と呼んでいます。

イベント は種類によって処理される優先順位が決まっています。例えば キーイベント では、ユーザは最前面のレスポンダを操作しているために、最子階層のフォーカスされている レスポンダ が最初に イベント を処理します。

図:イベント処理の優先順位

イベント処理の優先順位

このような動作を実現するためには、親レスポンダ子レスポンダ より後でイベントを処理しなければいけません。そこで、ハンドラ には 前置ハンドラ後置ハンドラ の2種類が存在します。

前置ハンドラ は子レスポンダにイベントを配信する前に呼び出される ハンドラ です。一方、後置ハンドラ は子レスポンダでの処理が終わった後に呼び出される ハンドラ です。

ハンドラ を登録するには RegisterHandler() 関数を使用します。ハンドラ に設定可能な規則は次の通りです。

ハンドラに設定可能な規則 解説
HANDLER_BEFORE 子レスポンダより先にイベントを処理する
HANDLER_AFTER 子レスポンダより後でイベントを処理する

ハンドラ を作成するには、次のマクロを使用します。( 特殊化バージョンもあります。)

マクロ名 解説
HANDLER_DECLARE_BOOLEVENT 宣言用マクロ
HANDLER_IMPLEMENT_BOOLEVENT 実装用マクロ

例:キーハンドラの登録と実装
class MyWin : public SFRPlainWindow {
    public:
        explicit MyWin(Void) static_throws;
    private:
        HANDLER_DECLARE_BOOLEVENT(OnKey)
};

MyWin::MyWin(Void) static_throws : SFRPlainWindow(...)
{
    if (static_try()) {

        // キーイベントのハンドラ OnKey を登録する

        static_throw(RegisterHandler(SFEVT_KEY, 
                                     HANDLER_AFTER, 
                                     HANDLER_FUNCTION(OnKey)));
    }
    return;
}
HANDLER_IMPLEMENT_BOOLEVENT(MyWin, OnKey, event)
{
    switch (event.GetP16()) {
        case AVK_SELECT:
            DoSelect();
            return true;
        case AVK_CLR:
            DoClear();
            return true;
    }
    return false;
}

ハンドライベント を処理した場合は true を、そうでなければ false を返します。 ハンドラ の戻り値が true の場合、イベント処理エンジン は必要に応じて再描画イベント を生成して 描画処理エンジン を起動します。

描画処理

イベント処理エンジン が処理を終えると、必要に応じて 描画処理エンジン が起動されます。描画処理エンジン は、レスポンダの状態や配置関係に基づき最速で描画するように 描画ハンドラ を実行します。

イベントループ毎に必要な 描画ハンドラ だけが呼び出されるので、複雑な GUI 画面でもパフォーマンスは劣化しません。

描画のタイミング

画面はイベントループの最後に描画されます。ハンドラ が true を返し イベント処理エンジン描画処理エンジン を起動すると、描画処理エンジン は再描画領域が登録されているかどうかを検査します。

シーケンス図:イベントが処理された場合

シーケンス図 : イベントが処理された場合

ハンドラfalse を返し 描画処理エンジン が起動されない場合や、再描画領域が存在しない場合は、描画しません。

シーケンス図:イベントが処理されなかった場合

シーケンス図 : イベントが処理されなかった場合

描画ハンドラ

すべての描画処理は、描画ハンドラ と呼ばれる描画処理専用のハンドラ内で行われます。

描画ハンドラハンドラ の一種で、RegisterHandler() 関数を使って登録します。ハンドラの実装には次のマクロを使用します。

マクロ名 解説
HANDLER_DECLARE_VOIDRENDER 宣言用マクロ
HANDLER_IMPLEMENT_VOIDRENDER 実装用マクロ

描画ハンドラハンドラ の引数に SFXGraphics クラスのインスタンスを取得します。ハンドラ では、このインスタンスを使って描画します。

例:描画ハンドラの登録と実装
class MyWin : public SFRPlainWindow {
    public:
        explicit MyWin(Void) static_throws;
    private:
        HANDLER_DECLARE_VOIDRENDER(OnRender)
};

MyWin::MyWin(Void) static_throws : SFRPlainWindow(...)
{
    if (static_try()) {

        // 描画ハンドラ OnRender を登録する

        static_throw(RegisterHandler(SREVT_RESPONDER_RENDER,
                                     SRP16_RENDER_CONTENT, 
                                     HANDLER_BEFORE,
                                     HANDLER_FUNCTION(OnRender)));
    }
    return;
}

HANDLER_IMPLEMENT_VOIDRENDER(MyWin, OnRender, graphics)
{
    // 描画ハンドラ内での描画には graphics インスタンスを使う

    graphics->FillRectangle(GetContentWorld(), 
                               SFXRGBColor(0x00, 0xFF, 0xFF, 0x00));
    return;
}

再描画領域の登録

再描画が必要なタイミングでは再描画領域を登録し、実際の描画は 描画ハンドラ で行います。

例えば、ラベルコントロールにおいて SetText() 関数でラベルの文字列を変更するとラベルコントロールを再描画しなければいけません。 この場合、SetText() 関数内では InvalidateContent() 関数を利用して再描画領域を 描画処理エンジン に登録します。

そして、描画処理エンジンに制御が移った段階で、登録された再描画領域は他で登録されたものと一括して再描画されます。

例:再描画領域の登録
class MyLabel : public SFRControl {
    public:
        explicit MyLabel(SFRResponderPtr director) static_throws;
        Void SetText(SFXAnsiStringConstRef param);
    private:
        HANDLER_DECLARE_VOIDRENDER(OnRender)
};

MyLabel::MyLabel(SFRResponderPtr director) static_throws
 : SFRControl(director, ...)
{
    if (static_try()) {

        // 描画ハンドラ OnRender を登録する

        static_throw(RegisterHandler(SREVT_RESPONDER_RENDER,
                                     SRP16_RENDER_CONTENT, 
                                     HANDLER_BEFORE,
                                     HANDLER_FUNCTION(OnRender)));
    }
    return;
}

Void MyLabel::SetText(SFXAnsiStringConstRef param)
{
    if (param != _text) {
        _text = param;

        // 再描画すべきことを描画処理エンジンに登録する

        InvalidateContent();
    }
    return;
}

HANDLER_IMPLEMENT_VOIDRENDER(MyLabel, OnRender, graphics)
{
    SFXRectangle rect(GetContentWorld());

    // 実際の描画は描画ハンドラ内で行う

    graphics->FillRectangle(rect, SFXRGBColor(0xFF, 0xFF, 0xFF, 0x00));
    graphics->DrawString(_text, rect, SFXRGBColor(0x00, 0x00, 0x00, 0x00));
    return;
}

再描画領域を登録すると、イベントループの最後に 描画ハンドラ が呼び出されます。

InvalidateContent() 関数には矩形の引数を取るオーバーロード関数を使って再描画領域を限定すると、高速な再描画処理を実現できます。

明示的な再描画

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

シーケンス図:コールバック呼び出し

シーケンス図 : コールバック呼び出し

ネットワーク処理が完了したタイミングなどで画面を再描画するには、描画処理エンジン を明示的に起動する必要があります。

シーケンス図:描画処理エンジンの起動

シーケンス図 : 描画処理エンジンの起動

SFRApplication クラスに再描画イベントを送信することで、描画処理エンジン を明示的に起動できます。( SFRApplication クラス以外のクラスへの再描画イベントの送信は保証されていません。)

例:明示的な再描画
CALLBACK_IMPLEMENT_SFXHTTPCONNECTION(MyWin, OnConnect, error)
{
    SFRApplicationPtr app(SFRApplication::GetInstance());

    // ラベルの文字列を変更する

    _label->SetText("OnConnect");

    // 描画処理エンジンを明示的に起動する
    //
    // 以下のコードを記述しないと画面が再描画されず、
    // SetText() 関数の結果が画面に反映されない

    app->Invoke(SFXEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, false));
    return;
}

SFXEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, false) イベントの送信で描画処理エンジンを明示的に起動できます。

SFXEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, true) イベントを送信すると、再描画領域の登録に関係なく描画ハンドラが呼び出され、すべてのレスポンダが強制的に再描画されます。