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

3.4. メニュー

3.4.1. テキストメニュー

図のようにウィンドウ上にテキストメニュー(SFZTextMenu)を配置するコードを追加します。

メニュー項目を選択することによって、配色やメニュースタイルを変更できます。

図 3.23. テキストメニュー

テキストメニュー

テキストメニューを配置するウィンドウを定義します。

例 3.44. MenuWindow の定義

// SFZWindowから継承します。
SFMTYPEDEFRESPONDER(MenuWindow)
class MenuWindow : public SFZWindow {
    SFMSEALRESPONDER(MenuWindow)
    SFMRESPONDERINSTANTIATEFOUR(MenuWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder) // SFYResponder から MenuWindow に至る継承順序
public:
    // 小文字アルファベットまたは記号4文字からなるタイプは予約されているので
    // MenuWindow のタイプを大文字アルファベット('M', 'E', 'N', 'U')で定義する
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'E', 'N', 'U')
    };
    SFMTYPEDEFTYPE(CodeEnum)
    // テキストメニューの色構造体
    SFMTYPEDEFSTRUCT(ColorRec)
    struct ColorRec {
        SFMTYPEDEFSTRUCT(AtomRec)
        struct AtomRec {
            // ColorRec::AtomRec -> ColorRec への暗黙の型変換演算子を宣言するマクロ
            SFMUTILITYATOMDECLARE(ColorRec)
            SFXRGBColor::AtomRec light;
            SFXRGBColor::AtomRec base;
            SFXRGBColor::AtomRec dark;
            SFXRGBColor::AtomRec titleFore;
            SFXRGBColor::AtomRec titleBack;
            SFXRGBColor::AtomRec itemFore;
            SFXRGBColor::AtomRec itemBack;
            SFXRGBColor::AtomRec selFore;
            SFXRGBColor::AtomRec selBack;
        };
        SFXRGBColor light;
        SFXRGBColor base;
        SFXRGBColor dark;
        SFXRGBColor titleFore;
        SFXRGBColor titleBack;
        SFXRGBColor itemFore;
        SFXRGBColor itemBack;
        SFXRGBColor selFore;
        SFXRGBColor selBack;
    };
private:

    // テキストメニューの宣言
    SFZTextMenuSmp _textMenu;
public:
    static MenuWindowSmp NewInstance(SFCErrorPtr exception = null);

    // テキストメニューの作成と配置
    SFCError Make(Void);
protected:
    explicit MenuWindow(Void) static_throws;
    virtual ~MenuWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    // テキストメニューの色をまとめて設定する関数の宣言
    Void SetMenuColors(ColorRecConstRef color);

    // テキストメニュー用ハンドラの宣言
    XANDLER_DECLARE_VOIDRESULT(OnMenuResult)
    // キーハンドラの宣言
    XANDLER_DECLARE_BOOLEVENT(OnKey)
};

// ColorRec と ColorRec::AtomRec のクラスサイズが等しいことを保障する
SFMCONCEPTGLOBALTWO(concept_size_eq_type, MenuWindow::ColorRec, MenuWindow::ColorRec::AtomRec)
// ColorRec::AtomRec -> ColorRec への暗黙の型変換演算子を実装するマクロ
SFMUTILITYATOMIMPLEMENT(MenuWindow::ColorRec, AtomRec)
[Note] AtomRec 構造体

AtomRec 構造体とは、クラスまたは構造体のメンバと同じメモリ配置を持つ C 言語の構造体と互換性のある POD(Plain Old Data) 構造体のことです。 詳細は、RealView コード生成ツールの最適化(Sophia Cradle 社 Web サイト)をご覧ください。

クラスや構造体に AtomRec 構造体を定義すると、 クラスや構造体の初期化コードのサイズと実行速度を最適化することができます。 このとき、AtomRec 構造体を定義するクラスでは仮想関数、多重継承、仮想継承を利用してはいけません。

AtomRec 構造体は、SFXEvent, SFXEventRange, 図形と色のクラスなどで利用されています。

これらのクラスでは、SFXxxxxx → SFXxxxxx::AtomRec および SFXxxxxx::AtomRec → SFXxxxxx の型変換をサポートする atomic_cast 演算子が利用可能です。

atomic_cast 演算子は、 SFMUTILITYATOMICCASTDECLARE マクロと SFMUTILITYATOMICCASTIMPLEMENT マクロを利用して宣言・実装します。

AtomRec 構造体からクラスや構造体への暗黙の型変換演算子は、 SFMUTILITYATOMDECLARE マクロと SFMUTILITYATOMIMPLEMENT マクロを利用して実装します。

例 3.45. 使い方の例

SFXRGBColor::AtomRec    atom  = {0x00, 0xFF, 0x00, 0x00};   // 左から alpha, red, green, blue (SFXRGBColor と alpha の位置が違うので注意)
SFXRGBColor             color = atom;                       // 暗黙の型変換
SFXRGBColor::AtomRecPtr ptr   = atomic_cast(&color);        // atomic_cast 演算子

メニュー ウィンドウを定義します。

例 3.46. メニュー ウィンドウの実装

// 配色の設定
#define ATOM_MENU_ELEGANT_LIGHT             {0x00, 0xCB, 0xBD, 0xDC}
#define ATOM_MENU_ELEGANT_BASE              {0x00, 0xAB, 0xA7, 0xC4}
#define ATOM_MENU_ELEGANT_DARK              {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_ELEGANT_TITLE_BACK        {0x00, 0xBF, 0xE6, 0xCF}
#define ATOM_MENU_ELEGANT_TITLE_FORE        {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_ELEGANT_ITEM_FORE         {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_ELEGANT_ITEM_BACK         {0x00, 0xAB, 0xA7, 0xC4}
#define ATOM_MENU_ELEGANT_SELBACK           {0x00, 0xFC, 0xCD, 0xE5}
#define ATOM_MENU_ELEGANT_SELFORE           {0x00, 0xFF, 0x00, 0x00}
#define ATOM_MENU_CARNIVAL_LIGHT            {0x00, 0xFF, 0xCC, 0x00}
#define ATOM_MENU_CARNIVAL_BASE             {0x00, 0xFF, 0x7F, 0x00}
#define ATOM_MENU_CARNIVAL_DARK             {0x00, 0x0A, 0x50, 0xA1}
#define ATOM_MENU_CARNIVAL_TITLE_BACK       {0x00, 0x7D, 0x01, 0x77}
#define ATOM_MENU_CARNIVAL_TITLE_FORE       {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_CARNIVAL_ITEM_FORE        {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_CARNIVAL_ITEM_BACK        {0x00, 0xFF, 0x7F, 0x00}
#define ATOM_MENU_CARNIVAL_SELBACK          {0x00, 0xFF, 0xFF, 0x00}
#define ATOM_MENU_CARNIVAL_SELFORE          {0x00, 0x09, 0x30, 0x73}
#define ATOM_MENU_DYNAMIC_LIGHT             {0x00, 0xFF, 0xCC, 0x00}
#define ATOM_MENU_DYNAMIC_BASE              {0x00, 0x80, 0x9C, 0xC9}
#define ATOM_MENU_DYNAMIC_DARK              {0x00, 0x0A, 0x50, 0xA1}
#define ATOM_MENU_DYNAMIC_TITLE_BACK        {0x00, 0xFF, 0xFF, 0x00}
#define ATOM_MENU_DYNAMIC_TITLE_FORE        {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_DYNAMIC_ITEM_FORE         {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_DYNAMIC_ITEM_BACK         {0x00, 0x80, 0x9C, 0xC9}
#define ATOM_MENU_DYNAMIC_SELBACK           {0x00, 0xFF, 0x00, 0x00}
#define ATOM_MENU_DYNAMIC_SELFORE           {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_MONO_LIGHT                {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_MONO_BASE                 {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_DARK                 {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_TITLE_BACK           {0x00, 0x7F, 0x7F, 0x7F}
#define ATOM_MENU_MONO_TITLE_FORE           {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_ITEM_FORE            {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_MONO_ITEM_BACK            {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_SELBACK              {0x00, 0x7F, 0x7F, 0x7F}
#define ATOM_MENU_MONO_SELFORE              {0x00, 0xFF, 0xFF, 0xFF}

// コンストラクタ
MenuWindow::MenuWindow(Void) static_throws
{
    if (static_try()) {
        // レスポンダのタイプを設定する
        SetType(CODE_TYPE);

        // キーハンドラを登録する
        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
            XANDLER_INTERNAL(OnKey)
        ));
    }
}

// デストラクタ
MenuWindow::~MenuWindow(Void)
{
}

// MenuWindow の作成
MenuWindowSmp MenuWindow::NewInstance(SFCErrorPtr exception)
{
    return static_pointer_cast<MenuWindow>(Factory(:: new MenuWindow, exception));
}

// テキストメニュー作成
SFCError MenuWindow::Make(Void)
{
    SFCError error;

    // テキストメニューを作成する
    if ((_textMenu = SFZTextMenu::NewInstance(&error)) != null) {
        // 親レスポンダを MenuWindow に設定する
        error = _textMenu->SetParent(SFYApplication::GetInstance());
        if (error == SFERR_NO_ERROR) {
            // テキストメニュー用ハンドラを登録する
            error = _textMenu->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnMenuResult)
            );
            if (error == SFERR_NO_ERROR) {
                // テキストメニューのタイトルを設定する
                error = _textMenu->SetTitle("Menu");
                if (error == SFERR_NO_ERROR) {
                    // 文字列の初期化
                    SFXWideString item[] = {
                        "Elegant", "Carnival", "Dynamic", "Black", "***", "Scrollable Style",
                        "Page Style", "***", "Sample9", "Sample10", "Sample11", "Sample12"
                    };
                    // ダイレクトアクセスキーの初期化
                    AVKType key[lengthof(item)] = {
                        AVK_1, AVK_2, AVK_3, AVK_4, AVK_5, AVK_6,
                        AVK_7, AVK_8, AVK_9, AVK_STAR, AVK_0, AVK_POUND
                    };
                    // キーアイコンの初期化
                    WChar icon[lengthof(item)] = {
                        '1', '2', '3', '4', '5', '6',
                        '7', '8', '9', '*', '0', '#'
                    };
                    // 項目をまとめてテキストメニューに追加する
                    for (SInt16 i = 0; i < lengthof(item); ++i) {
                        if ((error = _textMenu->AppendItem(item[i], key[i], icon[i])) != SFERR_NO_ERROR) {
                            break;
                        }
                    }
                    if (error == SFERR_NO_ERROR) {
                        // メニューの LEFT キーを使わない (サブメニューがある場合に使用されるキー)
                        _textMenu->SetSelectLeftKey(null);
                        // メニューの RIGHT キーを使わない (サブメニューがある場合に使用されるキー)
                        _textMenu->SetSelectRightKey(null);
                        // テキストメニューの実領域を設定する
                        // MenuWindow の実領域と同じサイズになる
                        _textMenu->SetRealBound(SFXRectangle(SFXGrid::ZeroInstance(), GetRealBound().GetSize()));
                        // テキストメニューの状態を「可視+活性+操作可能+フォーカス」にまとめて設定する
                        _textMenu->SetState(true, true, true, true);
                        // テキストメニューを最前面に配置する
                        _textMenu->ToFront();
                    }
                }
            }
        }
    }
    return error;
}

// MenuWindow の描画ハンドラ
Void MenuWindow::HandleRenderRequest(SFXGraphicsPtr graphics) const
{
    unused(graphics);

    return;
}

// テキストメニュー用ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MenuWindow, OnMenuResult, invoker, reason, result)
{
    // テキストメニューの配色セット
    static ColorRec::AtomRecConst color[] = {
        {
            ATOM_MENU_ELEGANT_LIGHT,        ATOM_MENU_ELEGANT_BASE,         ATOM_MENU_ELEGANT_DARK,
            ATOM_MENU_ELEGANT_TITLE_FORE,   ATOM_MENU_ELEGANT_TITLE_BACK,   ATOM_MENU_ELEGANT_ITEM_FORE,
            ATOM_MENU_ELEGANT_ITEM_BACK,    ATOM_MENU_ELEGANT_SELFORE,      ATOM_MENU_ELEGANT_SELBACK
        },
        {
            ATOM_MENU_CARNIVAL_LIGHT,       ATOM_MENU_CARNIVAL_BASE,        ATOM_MENU_CARNIVAL_DARK,
            ATOM_MENU_CARNIVAL_TITLE_FORE,  ATOM_MENU_CARNIVAL_TITLE_BACK,  ATOM_MENU_CARNIVAL_ITEM_FORE,
            ATOM_MENU_CARNIVAL_ITEM_BACK,   ATOM_MENU_CARNIVAL_SELFORE,     ATOM_MENU_CARNIVAL_SELBACK
        },
        {
            ATOM_MENU_DYNAMIC_LIGHT,        ATOM_MENU_DYNAMIC_BASE,         ATOM_MENU_DYNAMIC_DARK,
            ATOM_MENU_DYNAMIC_TITLE_FORE,   ATOM_MENU_DYNAMIC_TITLE_BACK,   ATOM_MENU_DYNAMIC_ITEM_FORE,
            ATOM_MENU_DYNAMIC_ITEM_BACK,    ATOM_MENU_DYNAMIC_SELFORE,      ATOM_MENU_DYNAMIC_SELBACK
        },
        {
            ATOM_MENU_MONO_LIGHT,           ATOM_MENU_MONO_BASE,            ATOM_MENU_MONO_DARK,
            ATOM_MENU_MONO_TITLE_FORE,      ATOM_MENU_MONO_TITLE_BACK,      ATOM_MENU_MONO_ITEM_FORE,
            ATOM_MENU_MONO_ITEM_BACK,       ATOM_MENU_MONO_SELFORE,         ATOM_MENU_MONO_SELBACK
        }
    };

    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]);
                    break;
                case 5:
                    // メニュースタイルをスクロールスタイルに変更する
                    _textMenu->SetMenuStyle(SFZTextMenu::SCROLLABLE_STYLE);
                    break;
                case 6:
                    // メニュースタイルをページスタイルに変更する
                    _textMenu->SetMenuStyle(SFZTextMenu::PAGE_STYLE);
                    break;
            }
            break;
        case SFP16_RESULT_ESCAPE:   // ESCAPE キー(デフォルトではクリアキー)が押されたとき
            // MenuWindow を閉じる
            this->Terminate();
            break;
    }
    return;
}

// テキストメニューの色をまとめて設定する
Void MenuWindow::SetMenuColors(ColorRecConstRef color)
{
    if (_textMenu != null) {
        // メニュータイトルの前景色を設定する
        _textMenu->SetTitleForeColor(color.titleFore);
        // メニュータイトルの背景色を設定する
        _textMenu->SetTitleBackColor(color.titleBack);
        // 選択されたメニュー項目の背景色を設定する
        _textMenu->SetSelBackColor(color.selBack);
        // 選択されたメニュー項目の前景色を設定する
        _textMenu->SetSelForeColor(color.selFore);
        // メニューのベベル カラーを設定
        _textMenu->SetBevelColor(SFXBevelColor(color.light, color.base, color.dark));
        // すべてのメニュー項目の前景色と背景色を設定する
        for (SInt16 i = 0; i < _textMenu->GetItemCount(); ++i) {
            _textMenu->SetItemForeColor(i, color.itemFore);
            _textMenu->SetItemBackColor(i, color.itemBack);
        }
    }
    return;
}
[Tip] メニューの親レスポンダ

メニューはユーザーの選択が終わると画面上から消える一時的なレスポンダです。

メニューの親レスポンダは最前面に表示されているウィンドウなどのレスポンダではなく、 アプリケーションクラス[デフォルトではルート(SFZRoot)]に設定します。

親レスポンダをアプリケーションクラスにすると、 画面全体の中から最適な位置にメニューを表示できます。

[Note] テキストメニューのハンドラ登録

テキストメニューは、項目が選択されると、結果イベントが発生します。

SFYResponder::RegisterHandler 関数を利用して結果イベントのハンドラをテキストメニューに登録します。

関連情報 : 結果イベント[SFEVT_RESPONDER_RESULT] | 結果イベント専用ハンドラ[XANDLER_DECLARE_VOIDRESULT]

[Note] ダイレクトアクセスキー

テキストメニューの項目にキーを割り当てることにより、そのキーを押すことで項目を選択できる機能です。

Hello World 画面で2キーを押すとテキストメニュー ウィンドウを表示するようにします。

クリアキーを押すと、テキストメニュー ウィンドウは閉じます。

例 3.47. HelloWorld の変更

// HelloWorld の変更
SFMTYPEDEFCLASS(HelloWorld)
class HelloWorld : public SFYApplication {
    SFMSEALCOPY(HelloWorld)
private:
    MyWindowSmp _myWindow;

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

    // MenuWindow のスマートポインタ
    MenuWindowSmp _menuWindow;
public:
    static SFCInvokerPtr Factory(Void);
private:
    explicit HelloWorld(Void) static_throws;
    virtual ~HelloWorld(Void);

    // MyWindow 作成
    SFCError MakeMy(Void); // 名称を Make から MakeMy に変更

    // MenuWindow 作成
    SFCError MakeMenu(Void);

    XANDLER_DECLARE_VOIDRENDER(OnRenderRequest)
    XANDLER_DECLARE_BOOLEVENT(OnKey)
};

// MenuWindow の作成
SFCError HelloWorld::MakeMenu(Void)
{
    SFCError error(SFERR_NO_ERROR);

    if ((_menuWindow = MenuWindow::NewInstance(&error)) != null) {
        error = _menuWindow->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {
            _menuWindow->SetRealBound(GetLocalBound());
            _menuWindow->SetState(true, true, true, true);
            _menuWindow->ToFront();
            _menuWindow->Make();
        }
    }

    return error;
}

// 描画ハンドラ
XANDLER_IMPLEMENT_VOIDRENDER(HelloWorld, OnRenderRequest, invoker, reason, graphics)
{
    unused(invoker);

    // 描画領域を白色で塗りつぶす
    graphics->FillRectangle(GetLocalBound(), COLOR_WHITE);

    // *** 太字が修正部分

    graphics->DrawSingleText("1key - Controls", SFXGrid(20, 20), GetLocalBound(), COLOR_BLACK);
    graphics->DrawSingleText("2key - TextMenu", SFXGrid(20, 40), GetLocalBound(), COLOR_BLACK);

    return;
}

// キーハンドラ
XANDLER_IMPLEMENT_BOOLEVENT(HelloWorld, OnKey, invoker, event)
{
    unused(invoker);

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

    // アプリケーションクラスに可視な子レスポンダが存在しないときだけキー処理をする
    if (this->GetChildFront(true, false, false, false) == null) {
        switch (event.GetP16()) {
            case AVK_SELECT:
                Terminate();
                return true;
            case AVK_1:
                TRACE("1 key");
                if (MakeMy() != SFERR_NO_ERROR) { 
                    return false;
                }
                return true;
            case AVK_2: // 2キーが押されたとき
                TRACE("2 key");
                // MenuWindow 呼び出し
                if (MakeMenu() != SFERR_NO_ERROR) {
                    return false;
                }
                return true; // キーイベントを処理したので true を返す
        }
    }
    return false; // キーイベントを処理しないときは false を返す
}

MyWindow が処理しなかったキーは、親レスポンダである HelloWindow が処理します。

MyWindow が開いているときは HelloWorld がキー処理をして欲しくないので HelloWorld に可視な子レスポンダがあるときのみキー処理をします。

シミュレータでの実行結果です。

  1. 2キーを押すと、メニュー ウィンドウが表示されます。
  2. カーソル キーで項目を選択するか、ダイレクト キーで項目を選択します。

図 3.24. 初期 ウィンドウ (HelloWorld)

初期 ウィンドウ (HelloWorld)

図 3.25. MenuWindow (各種画面例)

MenuWindow (各種画面例)

図 3.26. メニュー項目が押されたときのデバッグウィンドウ

メニュー項目が押されたときのデバッグウィンドウ

関連情報 : メニュー(基礎編) | SFYResponder::GetChildFront