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

3.6. メニュー

SFYMenu クラスは、 項目のリストから 1 つの項目を選択するためのメニューを実装するための基底クラスです。

このクラスは、 操作キーまたは ESCAPE キーを押下すると、 デフォルトのキーハンドラにより、 SFYMenu は結果イベントを受信します。 また、SFYMenu は、 一定時間経過後に自動的に閉じる機能も提供します。

具象メニューとして、 テキストメニュー(SFZTextMenu)、 グリッドメニュー(SFZGridMenu)、 フレックスリストメニュー(SFZFlexListMenu)の 3 つがあります。

この節では、 テキストメニュー(SFZTextMenu)について解説します。

3.6.1. テキストメニュー

下図のテキストメニュー(SFZTextMenu)を作成します。

図 3.24. テキストメニュー

テキストメニュー

選択項目を操作する(実際に選択する)と、 配色やメニュースタイルが変更されます。

helloworld アプリケーションクラスにメニューを作成する関数を追加します。 メニューの色をまとめて設定するための構造体や関数も定義します。

例 3.45. helloworld アプリケーションクラスとカスタム色構造体の定義

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

    SFMSEALCOPY(helloworld)  // インスタンスのコピーを禁止するマクロ
private:
    MyWindowSmp _myWindow;

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

    // SFZTextMenu のスマートポインタ
    SFZTextMenuSmp _textMenu;
public:
    static SFCInvokerPtr Factory(Void);
private:
    explicit helloworld(Void) static_throws;
    virtual ~helloworld(Void);

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

    // SFZTextMenu 作成
    SFCError MakeMenu(Void);

    // テキストメニューの色をまとめて設定する関数
    Void SetMenuColors(UserColorConstRef color);

    XANDLER_DECLARE_VOIDRENDER(OnRenderRequest)  // 描画ハンドラ
    XANDLER_DECLARE_BOOLEVENT(OnKey)             // キーハンドラ

    // テキストメニューの結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnMenuResult)
};

// カスタム色構造体(メニューの配色設定に利用する)
SFMTYPEDEFSTRUCT(UserColor)
struct UserColor {
    SFMTYPEDEFSTRUCT(AtomRec)
    struct AtomRec {

        // UserColor::AtomRec -> UserColor への暗黙的変換を宣言するマクロ
        SFMUTILITYATOMDECLARE(UserColor)

        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;
};

// 2 つのクラスサイズが等しいことを保障する(サイズが異なる場合はコンパイルエラーとなる)
SFMCONCEPTGLOBALTWO(concept_size_eq_type, UserColor, UserColor::AtomRec)

// UserColor::AtomRec -> UserColor への暗黙的変換を実装するマクロ
SFMUTILITYATOMIMPLEMENT(UserColor, AtomRec)
[Note] AtomRec 構造体

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.46. 使い方の例

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

SFMCONCEPTGLOBALTWO(concept_size_eq_type, Type_A, Type_B) マクロは、 Type_A 型と Type_B 型のサイズが等しいことを保証します。 Type_A 型と Type_B 型のサイズが異なる場合は、コンパイルエラーとなります。 詳細は、以下のこのマクロに関する定義を参照してください。

#define     SFMCONCEPTGLOBALTWO(TYPE, ONE, TWO) \
template class static_concept_special< TYPE<ONE, TWO> >;

template <typename T, typename M>
class concept_size_eq_type {
    public:
        inline  void                            constraints                     (void)
                {
                    char                        c[(sizeof(T) == sizeof(M)) ? (1) : (-1)];

                    (void)(c);
                    return;
                }// constraints //
};

template <typename T>
class static_concept_special {
    public:
        inline  static  void                    constraints                     (void)
                {
                    (void)(&T::constraints);
                    return;
                }// constraints //
};

テキストメニューを作成し、 結果ハンドラで配色処理を行います。

例 3.47. テキストメニューの作成

// テキストメニューを作成する関数
SFCError helloworld::MakeMenu(Void)
{
    SFCError error(SFERR_NO_ERROR);

    // テキストメニューを作成する
    if ((_textMenu = SFZTextMenu::NewInstance(&error)) != null) {

        // 親をルートに設定する
        error = _textMenu->SetParent(GetThis());

        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("TextMenu");

                if (error == SFERR_NO_ERROR) {

                    // テキストの初期化
                    SFXWideString item[] = {
                        "Elegant", "Carnival", "Dynamic", "Black", "***", "Scrollable Style", "Page Style", "***",
                        "Sample9", "Sample10", "Sample11", "Sample12", "Sample13", "Sample14", "Sample15", "Sample16"
                    };

                    // アクセスキーの初期化
                    // 項目 0 〜 8、項目 15 にアクセスキーがセットされる
                    AVKType key[lengthof(item)] = {
                        AVK_1, AVK_2, AVK_3, AVK_4, AVK_5, AVK_6, AVK_7, AVK_8,
                        AVK_9, 0,     0,     0,     0,     0,     0,     AVK_0
                    };

                    // キーアイコンの初期化
                    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->InsertLast(item[i], key[i], icon[i])) != SFERR_NO_ERROR) {

                            break;
                        }
                    }

                    // 5 番目の項目を有効フラグを false にする
                    _textMenu->SetItemEnable(4, false);

                    // 8 番目の項目を有効フラグを false にする
                    _textMenu->SetItemEnable(7, false);

                    if (error == SFERR_NO_ERROR) {

                        // テキストメニューの←キーを使わない (サブメニューがある場合に使用されるキー)
                        _textMenu->SetSelectLeftKey(null);

                        // テキストメニューの→キーを使わない (サブメニューがある場合に使用されるキー)
                        _textMenu->SetSelectRightKey(null);

                        // テキストメニューの実領域をルートのローカル領域に設定する
                        _textMenu->SetRealBound(GetLocalBound());

                        // テキストメニューの状態を「可視+活性+操作可能+フォーカス」にまとめて設定する
                        _textMenu->SetState(true, true, true, true);

                        // テキストメニューを最前面に移動する
                        _textMenu->ToFront();
                    }
                }
            }
        }
    }
    return error;
}

// 色の定義
#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}

// テキストメニューの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(helloworld, OnMenuResult, invoker, reason, result)
{
    // テキストメニューの配色セット
    static UserColor::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 キー(デフォルトではクリアキー)が押されたとき

            // テキストメニューを閉じる
            invoker->Terminate();

            break;
    }
    return;
}

// テキストメニューの色をまとめて設定する
Void helloworld::SetMenuColors(UserColorConstRef 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] メニューの親レスポンダ

メニューの親レスポンダはアプリケーションクラス[SFYApplication がデフォルトで保持するルートSFZRoot)]にします。

ルートは、SFYApplication::GetThis 関数または SFYApplication::GetRoot 関数を呼び出して取得します。

[Note] テキストメニューの操作

テキストメニューは、選択キーによりメニューの選択項目を移動し、 操作キーまたはアクセスキーにより選択項目を操作します(実際に選択します)。

操作キーまたはアクセスキーを押下すると、 テキストメニューは結果イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベントを受信する 結果ハンドラをテキストメニューに登録することが可能です。

デフォルトでは、↑/↓キーが選択キーに設定されています。 選択キーは、 SFYMenu::SetSelectUpKey / SFYMenu::SetSelectDownKey 関数により変更可能です。

デフォルトでは、セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYMenu::SetOperateKey 関数により変更可能です。

アクセスキーとは、項目にキーを割り当て、 そのキーが押下されると、割り当てた項目を選択項目にし、かつ操作する(実際に選択する)機能です。 SFZTextMenu::SetItemAccessKey 関数を使用して設定します。

初期画面で 2 キーが押されると、 テキストメニューを表示するようにします。

例 3.48. 初期画面における描画処理とキー処理の変更

// helloworld アプリケーションクラスの描画ハンドラの実装
XANDLER_IMPLEMENT_VOIDRENDER(helloworld, OnRenderRequest, invoker, reason, graphics)
{
    unused(invoker);

    // helloworld アプリケーションクラス(ルート)のローカル領域(携帯電話の画面)の背景は
    // SetBackgroundColor 関数で設定した色(デフォルト: 白色)で塗り潰される

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

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

    return;
}

// helloworld アプリケーションクラスのキーハンドラの実装
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("key 1");
                if (MakeMy() != SFERR_NO_ERROR) {
                    return false;
                }
                return true;

            case AVK_2: // 2 キーが押されたとき

                TRACE("key 2");

                // メニューを作成する
                if (MakeMenu() != SFERR_NO_ERROR) {
                    return false;
                }

                // キーイベントを処理したので true を返す
                return true;
        }
    }
    // キーイベントを処理しなかったときは false を返す
    return false;
}

太字部分のコード

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

    // アプリケーションクラスに可視状態の子レスポンダが存在しないときだけキー処理をする
    if (this->GetChildFront(true, false, false, false) == null) {

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

の追加により、helloworld アプリケーションクラス(ルート[SFZRoot])は、 可視状態が "ON" である子レスポンダを持つとき、 すなわち、ウィンドウ(MyWindow)またはテキストメニュー(_textMenu)が表示されているとき、 helloworld::OnKey キーハンドラはキーイベントを処理しません。

このコードがなければ、 ウィンドウ(MyWindow)またはテキストメニュー(_textMenu)で処理されなかったキーイベントは、 helloworld::OnKey キーハンドラで処理されてしまいます。

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

  1. 2 キーを押すと、テキストメニューが表示されます。
  2. テキストメニューの項目を操作する(実際に選択する)には、 選択キーを使用して選択項目を移動し操作キー、または直接アクセスキーを押下します。

参照: メニューを表す抽象クラス[SFYMenu]

図 3.25. 初期画面(helloworld)

初期画面(helloworld)

図 3.26. テキストメニュー(各種画面例)

テキストメニュー(各種画面例)

図 3.27. 項目を選択したときのデバッグウィンドウ

項目を選択したときのデバッグウィンドウ

参照: メニュー(基礎編) | SFYResponder::GetChildFront