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

3.11. ユーザーイベント

ユーザーイベントは、 イベントタイプが SFEVT_USER_CLASS_BEGIN(0x8000) から SFEVT_USER_CLASS_END(0xFFFE)までの範囲内にある、 開発者が独自に定義するイベントです。

この節では、 ユーザーイベントの利用方法について解説します。

3.11.1. ユーザーイベントの利用

テキストメニューの結果ハンドラ内で選択したメニュー項目の配色情報を表すユーザーイベントをソフトキーコントロールやタブコントロール、 ウィンドウ用フレームに配信して、これらのレスポンダの配色を変更するコードを実装します。

図 3.49. ユーザーイベントの利用: 配色情報の配信

ユーザーイベントの利用: 配色情報の配信

カスタム色構造体を送信するためのユーザーイベント (以下、「SFEVT_USER_COLOR イベント」)を定義します。

具体的には、 SFEVT_USER_COLOR イベント値の定義と SFEVT_USER_COLOR イベント用ハンドラマクロの定義を行います。

例 3.73. SFEVT_USER_COLOR イベント値の定義

// SFEVT_USER: ユーザーイベントの開始値
#define SFEVT_USER_COLOR    (SFEVT_USER + 0x0001)
#define SFP16_USER_COLOR    0

例 3.74. SFEVT_USER_COLOR イベント用ハンドラマクロの定義

// 宣言用マクロ
#define     XANDLER_DECLARE_VOIDUSERCOLOR(FUNCTION) \
static Bool XANDLER_FUNCTION(FUNCTION)(SFYResponderPtr invoker, SFXEventConstRef event, VoidPtr reference); \
Void FUNCTION(SFYResponderPtr invoker, UInt16 reason, UserColorPtr color);

// 実装用マクロ
// * 32 ビット値として、UserColor 構造体のポインタ値が入る
#define     XANDLER_IMPLEMENT_VOIDUSERCOLOR(TYPE, FUNCTION, INVOKER, REASON, COLOR) \
Bool XANDLER_FUNCTION(TYPE::FUNCTION)(SFYResponderPtr invoker, SFXEventConstRef event, VoidPtr reference) \
{ \
    static_cast<TYPE*>(reference)->FUNCTION(invoker, event.GetP16(), reinterpret_cast<UserColorPtr>(event.GetP32())); \
    return true; \
} \
\
Void TYPE::FUNCTION(SFYResponderPtr INVOKER, UInt16 REASON, UserColorPtr COLOR)

SFEVT_USER_COLOR イベントのハンドラの宣言は、

XANDLER_DECLARE_VOIDUSERCOLOR(OnColor)

SFEVT_USER_COLOR イベントのハンドラの登録は、

RegisterHandler(
    SFXEventRange(SFEVT_USER_COLOR, SFEVT_USER_COLOR, SFP16_USER_COLOR, SFP16_USER_COLOR),
    XANDLER_INTERNAL(OnColor)
);

SFEVT_USER_COLOR イベントのハンドラの実装は、

XANDLER_IMPLEMENT_VOIDUSERCOLOR(helloworld, OnColor, invoker, reason, color)
{

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

    return;
}

というように、 レスポンダイベントのハンドラの場合と同様に行います。

しかし、 これだけでは SFEVT_USER_COLOR イベントはターゲットとなるレスポンダに配信されません。

配信エンジンSFYDistributer) のトレーサSFEVT_USER_COLOR イベントの配信規則を登録する処理も必要です。

この処理は、 helloworld アプリケーションクラスのコンストラクタに以下のように記述します。

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

// コンストラクタ
helloworld::helloworld(Void) static_throws
{
    if (static_try()) {

        // *** 太字が追加部分

        SFYDistributerPtr distributer;

        if ((distributer = GetDistributer()) != null) {

            // 配信エンジンのトレーサにユーザーイベントに関する配信規則を登録する
            // * 背面からすべての可視レスポンダに配信する(重複フラグ: true)
            static_throw(distributer->RegisterTracer(
                SFXEventRange(SFEVT_USER_COLOR, SFEVT_USER_COLOR, SFP16_USER_COLOR, SFP16_USER_COLOR),
                SFYTracer::ORDER_BACKWARD, SFYTracer::STATE_VISIBLE, true
            ));

            if (static_try()) {

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

            }
        }
        else {
            static_throw(SFERR_FAILED);
        }
    }
}

テキストメニューの結果ハンドラを変更して、 実際に SFEVT_USER_COLOR イベントをレスポンダに配信するようにします。

例 3.76. SFEVT_USER_COLOR イベントの配信

// テキストメニューの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(helloworld, OnMenuResult, invoker, reason, result)
{

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

    switch (reason) {

        case SFP16_RESULT_OK:

            TRACE("'%S' is selected", _textMenu->GetItemText(static_cast<SInt16>(result)).GetCString());
            switch (result) {
                case 0:
                case 1:
                case 2:
                case 3: {
                    SetMenuColors((color[result]));
                    MakeColorDialog(_textMenu->GetItemText(result), (color[result]));

                    // *** 太字が追加部分

                    // ルートを取得する
                    SFYResponderSmp root;
                    if ((root = GetRoot()) != null) {

                        // ルートから配色イベントを配信する
                        // * 32 ビット値に UserColor クラスへのポインタを引数に渡している
                        error = root->Distribute(
                            SFXEvent(SFEVT_USER_COLOR, SFP16_USER_COLOR, reinterpret_cast<UInt32>(&color[result]))
                        );

                    }
                    break;
                }

            }
            break;

        // ...(省略)...
    }
    return;
}
[Note] Distribute 関数

イベントを子レスポンダにも配信する必要があるときは、 SFYResponder::Distribute 関数(タイプ 1)を利用します。 この場合、トレーサに配信規則を登録する必要があります。

SFYResponder::InvokeBackward / SFYResponder::InvokeForward 関数は、 イベントをある特定のレスポンダへコールバック型で送信する場合に利用します。 この場合、イベントは送信先レスポンダの子レスポンダに配信されません。

[Caution] イベントループ内で新規に作成されたレスポンダへのイベント配信について

ダイアログとそのフレームは、配色ハンドラで配色設定を行っていてないことに注意してください。

OnMenuResult ハンドラでは、 同じイベントループ内でダイアログ作成処理と配色イベントの配信を行っています

同じイベントループ内で新規に作成したレスポンダ(上の例では、ダイアログとそのフレーム)には SFYResponder::Distribute 関数(タイプ 1)を利用してイベントを配信できません(後述※)。

そのため、ダイアログやそのフレームに配色イベントを受信するハンドラを登録しても、 ダイアログやそのフレームは配色イベントを受信しません(ハンドラが呼び出されることはありません)。

SFYResponder::Distribute 関数は、 内部的にイベントループの最初に呼び出されますが、 このとき、 ルート以下のレスポンダツリーの構造を記憶します。 その後、同じイベントループ内で明示的に SFYResponder::Distribute 関数を呼び出した場合、 イベント配信の対象はこのときに記憶されたレスポンダツリーが対象となります。 つまり、イベントループ内で新たに生成されたレスポンダに対して SFYResponder::Distribute 関数を利用してイベントは配信できません。 ただし、このイベントループを抜ければ、このイベントループで作成されたレスポンダにイベントを配信することが可能です。

参照: ハンドラ | ハンドラリスト | コールバック型 | 配信型 | 配信エンジン | トレーサ イベント | SFYResponder::InvokeForward | SFYResponder::InvokeBackward | SFYResponder::Distribute

ソフトキーコントロール、ウィンドウ用フレーム、タブコントロールなどのレスポンダに SFEVT_USER_COLOR イベントを受信するハンドラを登録します。 ハンドラには、受信した SFEVT_USER_COLOR イベントの情報を基にして、 レスポンダの配色を変更する処理を記述します。

例 3.77. 配色ハンドラの登録

// helloworld アプリケーションクラスの定義
SFMTYPEDEFCLASS(helloworld)  //  便利な型を生成するマクロ
class helloworld : public SFYApplication {

    SFMSEALCOPY(helloworld)  // インスタンスのコピーを禁止するマクロ
private:
    MyWindowSmp _myWindow;
    ItsWindowSmp _itsWindow;
    SFZTextMenuSmp _textMenu;
    TabWindowSmp _tabWindow;
    SFZSoftKeyControlSmp _softkey;
    SFZTitlePlainFrameSmp _windowFrame;
    SFZTitleBevelFrameSmp _dialogFrame;
public:
    static SFCInvokerPtr Factory(Void);
private:

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

    // *** 太字が追加部分

    // ソフトキーコントロールの配色ハンドラ
    XANDLER_DECLARE_VOIDUSERCOLOR(OnSoftColor)

    // MyWindow 用フレームの配色ハンドラ
    XANDLER_DECLARE_VOIDUSERCOLOR(OnWindowFrameColor)
};

// TabWindow クラスの定義
SFMTYPEDEFRESPONDER(TabWindow)  //  便利な型を生成するマクロ
class TabWindow : public SFZWindow {

    SFMSEALRESPONDER(TabWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(TabWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:
    enum CodeEnum {
        CODE_TYPE = four_char_code('T', 'A', 'B', 'W')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:
    SFZTabControlSmp _tab;
public:
    static TabWindowSmp NewInstance(SFCErrorPtr exception = null);
    SFCError Make();
protected:
    explicit TabWindow(Void) static_throws;
    virtual ~TabWindow(Void);
private:
    XANDLER_DECLARE_BOOLEVENT(OnKey)  // キーハンドラ

    // タブコントロールの配色ハンドラ
    XANDLER_DECLARE_VOIDUSERCOLOR(OnTabColor)
};

// コンストラクタ
helloworld::helloworld(Void) static_throws
{
    if (static_try()) {

        SFYDistributerPtr distributer;
        if ((distributer = GetDistributer()) != null) {

            // 配信エンジンのトレーサにユーザーイベントの配信ルールを登録する
            static_throw(
                distributer->RegisterTracer(
                    SFXEventRange(SFEVT_USER_COLOR, SFEVT_USER_COLOR, SFP16_USER_COLOR, SFP16_USER_COLOR),
                    SFYTracer::ORDER_BACKWARD, SFYTracer::STATE_VISIBLE, true)
            );
            if (static_try()) {

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

                if ((_softkey = SFZSoftKeyControl::NewInstance()) != null) {
                        static_throw(_softkey->SetParent(GetThis()));
                        if (static_try()) {

                            // ソフトキーコントロールの配色ハンドラを登録する
                            static_throw(
                                _softkey->RegisterHandler(
                                    SFXEventRange(SFEVT_USER_COLOR, SFEVT_USER_COLOR, SFP16_USER_COLOR, SFP16_USER_COLOR),
                                    XANDLER_INTERNAL(OnSoftColor))
                            );

                            if (static_try()) {

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

                            }
                        }
                    }
                }
                else {
                    static_throw(SFERR_NO_MEMORY);
                }
            }
        }
        else {
            static_throw(SFERR_FAILED);
        }
    }
}

// MyWindow 用フレームを作成する関数
SFCError helloworld::MakeWindowFrame(SFXWideStringConstRef title)
{
    SFCError error(SFERR_NO_ERROR);

    if ((_windowFrame = SFZTitlePlainFrame::NewInstance(&error)) != null) {

        // MyWindow 用フレームの配色ハンドラを登録する
        error = _windowFrame->RegisterHandler(
            SFXEventRange(SFEVT_USER_COLOR, SFEVT_USER_COLOR, SFP16_USER_COLOR, SFP16_USER_COLOR),
            XANDLER_INTERNAL(OnWindowFrameColor)
        );

        if (error == SFERR_NO_ERROR) {

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

        }
    }
    return error;
}

// TabWindow の子レスポンダ(タブコントロール)の作成
SFCError TabWindow::Make()
{
    MyWindowSmp myWindow;
    CustomContainerSmp customContainer;
    SFCError error(SFERR_NO_ERROR);

    if ((_tab = SFZTabControl::NewInstance(&error)) != null) {

        error = _tab->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            // タブコントロールに配色ハンドラを登録する
            error = _tab->RegisterHandler(
                SFXEventRange(SFEVT_USER_COLOR, SFEVT_USER_COLOR, SFP16_USER_COLOR, SFP16_USER_COLOR),
                XANDLER_INTERNAL(OnTabColor)
            );

            if (error == SFERR_NO_ERROR) {

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

            }
        }
    }
    return error;
}

以下は、配色ハンドラとメッセージダイアログの実装です。

例 3.78. 配色ハンドラの実装

// ソフトキーコントロールの配色ハンドラの実装
XANDLER_IMPLEMENT_VOIDUSERCOLOR(helloworld, OnSoftColor, invoker, reason, color)
{
    unused(invoker);
    unused(reason);

    // ソフトキーコントロールの背景色を設定する
    _softkey->SetBackgroundColor(color->itemBack);

    // ソフトキーコントロールのラベルの色を設定する
    // * 何も指定しなければソフトキーメニューはキー 0 の色を参照する
    // * ソフトキーメニューの色キーは変更していないので、
    // * 今回、すべてのソフトキーメニューが キー 0 の色を参照している
    _softkey->RegisterFrameColor(0, color->dark);
    _softkey->RegisterBackColor(0, color->titleBack);
    _softkey->RegisterForeColor(0, color->titleFore);

    return;
}

// MyWindow 用フレームの配色ハンドラの実装
XANDLER_IMPLEMENT_VOIDUSERCOLOR(helloworld, OnWindowFrameColor, invoker, reason, color)
{
    unused(invoker);
    unused(reason);

    // フレームのヘッダー領域の色を設定する
    _windowFrame->SetHeaderColor(color->titleBack);

    // フレームのタイトルの色を設定する
    _windowFrame->SetTextColor(color->titleFore);

    return;
}

// タブコントロールの配色ハンドラの実装
XANDLER_IMPLEMENT_VOIDUSERCOLOR(TabWindow, OnTabColor, invoker, reason, color)
{
    unused(invoker);
    unused(reason);

    // タブコントロールの背景色を設定する
    _tab->SetBackgroundColor(color->base);

    // タブコントロールのヒント領域のべベルカラーを設定する
    _tab->SetHintBevelColor(SFXBevelColor(color->light, color->base, color->dark));

    // タブコントロールのタブ領域のべベルカラーを設定する
    _tab->SetTabBevelColor(SFXBevelColor(color->light, color->base, color->dark));

    // すべてのタブページを取得しタブページのタイトルテキストとヒントテキストの色を設定する
    for (SInt16 i = 0; i < _tab->GetPageCount(); ++i) {

        SFZTabPageSmp page = _tab->GetPage(i);
        page->SetTextColor(color->selFore);
    }

    return;
}

// メッセージダイアログを作成する関数
SFCError helloworld::MakeColorDialog(SFXWideStringConstRef title, UserColorConstRef color)
{
    SFZMessageDialogSmp dlg;
    SFCError error(SFERR_NO_ERROR);

    if ((error = MakeDialogFrame(title)) == SFERR_NO_ERROR) {

        if ((dlg = SFZMessageDialog::NewInstance(&error)) != null) {

            error = dlg->SetParent(GetThis());
            if (error == SFERR_NO_ERROR) {

                error = dlg->SetFrame(_dialogFrame);
                if (error == SFERR_NO_ERROR) {

                    error = dlg->RegisterHandler(
                        SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                        XANDLER_INTERNAL(OnDialogResult)
                    );
                    if (error == SFERR_NO_ERROR) {

                        error = dlg->SetMessageText("Color Changed.");
                        if (error == SFERR_NO_ERROR) {

                            error = dlg->SetButtonText("OK");
                            if (error == SFERR_NO_ERROR) {

                                // *** 太字が追加部分

                                // フレームのヘッダー領域のベベルカラーを設定する
                                _dialogFrame->SetHeaderColor(SFXBevelColor(color.light, color.titleBack, color.dark));

                                // フレームのタイトルの色を設定する
                                _dialogFrame->SetTextColor(color.titleFore);

                                dlg->SetBackgroundColor(color.selBack);

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

                            }
                        }
                    }
                }
            }
        }
    }
    return error;
}

以上ですべての実装が完了しました。

シミュレータでの実行結果は以下の通りです。

図 3.50. 配色: ダイアログ用フレームとソフトキーコントロール

配色: ダイアログ用フレームとソフトキーコントロール

図 3.51. 配色: ウィンドウ用フレームとタブコントロール

配色: ウィンドウ用フレームとタブコントロール

サンプルコードはこちらにあります。